Repository: kubernetes-sigs/kueue

Issue:
v1beta2: Delete .enable field from FairSharing API in config
<!-- Please only use this template for submitting clean up requests -->

**What would you like to be cleaned**:
- Remove `.enable` field from the FairSharing config API

**Why is this needed**:
With changes introduced by https://github.com/kubernetes-sigs/kueue/pull/4252 we have two modes of FairSharing. One of them is defined by `.preemptionStrategies`, and the second one by `.admissionFairSharing` field. Hence, the `.enable` field is redundant and allows user defining invalid states e.g. when `enabled=true` but both `preemptionStrategies` and `admissionFairSharing` are empty

Discussion:
@PBundyra: Is we agree on this change, I'll add it to v1beta2 wishlist
cc @mimowo @tenzen-y @gabesaba @mwielgus @gabesaba: I support this change. It will make the API more intuitive for users, and harder to misconfigure.

We may want to add to the v1beta2 wishlist:

A dedicated field for PreemptionFairSharing (perhaps with a better name), and one for AdmissionFairSharing, so it is crystal clear that preemptionStrategies is applying to this preemption mode. It will also allow those modes to be cleanly extended/modified as needed in the future@mimowo: sounds reasonable, the reason we introduced the "enable" field in knobs for some features was to enable administrators to enable / disable a feature without losing the configuration. This seemed like a reasonable use-case, but I'm not sure if this is "best practice" or commonly used strategy in other projects. 

If we decide on that I would suggest to apply the same approach to other configuration aspects, such as WaitForPodsReady too, for consistency and clear approach. I'm personally ok either way.

cc @tenzen-y @dgrove-oss wdyt?@kannon92: +1 on this. I do think we should maybe not delete a field from a beta API though. I would be happy with deprecation for a release and then a removal. 

We could also remove it when we promote the config apis to v1?@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@PBundyra: /remove-lifecycle stale@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/@mimowo: /remove-lifecycle stale@mimowo: Oh, we forgot to add it to v1beta2 wishlist. Let me add it still as a nice to have here: https://github.com/kubernetes-sigs/kueue/issues/7113@mimowo: /retitle v1beta2: Delete .enable field from FairSharing API in config@mimowo: I'm not totally sure if we want to drop the "enable" field, maybe just adding validation to prevent this: "Hence, the .enable field is redundant and allows user defining invalid states e.g. when enabled=true but both preemptionStrategies and admissionFairSharing are empty" - basically validate against "enable=true" and empty preemptionStrategies, wdyt @PBundyra ?@PBundyra: I still think having `.enable` field in the API is a bad design

>  the reason we introduced the "enable" field in knobs for some features was to enable administrators to enable / disable a feature without losing the configuration

I don't think we should be hinted by such a motivation. An admin is able to copy and save the old configuration and it's fairly easy. If there's no technical setback of improving this API, and it's mainly about ease of use for an admin (switching the field vs saving old configuration) then I wouldn't treat it as a blocker@mimowo: I'm ok with that, but then should we also adjust waitaforapodsaready and inetrnalCertManagement, seems desired to have consistent API.

 cc @mwysokin as it may potentially also impact upgrades @mbobrovskyi: /assign

Solution patch:
diff --git a/Makefile b/Makefile
index 24f896f69..898c9b89f 100644
--- a/Makefile
+++ b/Makefile
@@ -287,7 +287,6 @@ artifacts: kustomize yq helm ## Generate release artifacts.
 	$(KUSTOMIZE) build config/dev -o artifacts/manifests-dev.yaml
 	$(KUSTOMIZE) build config/alpha-enabled -o artifacts/manifests-alpha-enabled.yaml
 	$(KUSTOMIZE) build config/prometheus -o artifacts/prometheus.yaml
-	$(KUSTOMIZE) build config/visibility-apf -o artifacts/visibility-apf.yaml
 	$(KUSTOMIZE) build config/kueueviz -o artifacts/kueueviz.yaml
 	@$(call clean-manifests)
 	# Update the image tag and policy
diff --git a/Makefile-test.mk b/Makefile-test.mk
index d0faceee9..4d84b7fb2 100644
--- a/Makefile-test.mk
+++ b/Makefile-test.mk
@@ -100,7 +100,7 @@ test-integration: gomod-download envtest ginkgo dep-crds kueuectl ginkgo-top ##
 	$(PROJECT_DIR)/bin/ginkgo-top -i $(ARTIFACTS)/integration.json > $(ARTIFACTS)/integration-top.yaml
 
 .PHONY: test-multikueue-integration
-test-multikueue-integration: gomod-download envtest ginkgo dep-crds kueuectl ginkgo-top ## Run integration tests for Multikueue suite.
+test-multikueue-integration: gomod-download envtest ginkgo dep-crds ginkgo-top ## Run integration tests for MultiKueue suite.
 	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" \
 	PROJECT_DIR=$(PROJECT_DIR)/ \
 	KUEUE_BIN=$(PROJECT_DIR)/bin \
diff --git a/README.md b/README.md
index 28b353789..e81ae8bc6 100644
--- a/README.md
+++ b/README.md
@@ -36,11 +36,16 @@ Read the [overview](https://kueue.sigs.k8s.io/docs/overview/) and watch the Kueu
 - ✔️ Test Coverage:
   - ✔️ Unit Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-unit-main).
   - ✔️ Integration Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-integration-main)
+  - ✔️ Integration MultiKueue Tests [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-integration-multikueue-main)
   - ✔️ E2E Tests for Kubernetes
     [1.30](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-main-1-30),
     [1.31](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-main-1-31),
     [1.32](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-main-1-32),
     on Kind.
+  - ✔️ E2E TAS Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-tas-main)
+  - ✔️ E2E Custom Configs Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-customconfigs-main)
+  - ✔️ E2E Cert Manager Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-e2e-certmanager-main)
+  - ✔️ Performance Test [testgrid](https://testgrid.k8s.io/sig-scheduling#periodic-kueue-test-scheduling-perf-main)
 - ✔️ Scalability verification via [performance tests](https://github.com/kubernetes-sigs/kueue/tree/main/test/performance).
 - ✔️ Monitoring via [metrics](https://kueue.sigs.k8s.io/docs/reference/metrics).
 - ✔️ Security: RBAC based accessibility.
diff --git a/apis/kueue/v1beta1/workload_types.go b/apis/kueue/v1beta1/workload_types.go
index 699115583..cd65cd80a 100644
--- a/apis/kueue/v1beta1/workload_types.go
+++ b/apis/kueue/v1beta1/workload_types.go
@@ -560,13 +560,6 @@ const (
 	// because the LocalQueue is Stopped.
 	WorkloadEvictedByLocalQueueStopped = "LocalQueueStopped"
 
-	// WorkloadEvictedByDeactivation indicates that the workload was evicted
-	// because spec.active is set to false.
-	// Deprecated: The reason is not set any longer, it is only kept temporarily to ensure
-	// pre-existing deactivated workloads remain deactivated after upgrade from version
-	// prior to 0.10. The reason declaration can be removed in 0.11.
-	WorkloadEvictedByDeactivation = "InactiveWorkload"
-
 	// WorkloadDeactivated indicates that the workload was evicted
 	// because spec.active is set to false.
 	WorkloadDeactivated = "Deactivated"
diff --git a/charts/kueue/README.md b/charts/kueue/README.md
index 139bbf820..a014a93cb 100644
--- a/charts/kueue/README.md
+++ b/charts/kueue/README.md
@@ -73,7 +73,6 @@ The following table lists the configurable parameters of the kueue chart and the
 | `fullnameOverride`                                     | override the resource name                             | ``                                          |
 | `enablePrometheus`                                     | enable Prometheus                                      | `false`                                     |
 | `enableCertManager`                                    | enable CertManager                                     | `false`                                     |
-| `enableVisibilityAPF`                                  | enable APF for the visibility API                      | `false`                                     |
 | `enableKueueViz`                                       | enable KueueViz dashboard                              | `false`                                     |
 | `KueueViz.backend.image`                               | KueueViz dashboard backend image                       | `us-central1-docker.pkg.dev/k8s-staging-images/kueue/kueueviz-backend:main` |
 | `KueueViz.frontend.image`                              | KueueViz dashboard frontend image                      | `us-central1-docker.pkg.dev/k8s-staging-images/kueue/kueueviz-frontend:main` |
diff --git a/charts/kueue/templates/visibility-apf/flowschema.yaml b/charts/kueue/templates/visibility/flowschema.yaml
similarity index 93%
rename from charts/kueue/templates/visibility-apf/flowschema.yaml
rename to charts/kueue/templates/visibility/flowschema.yaml
index d43a31a72..18b2e8010 100644
--- a/charts/kueue/templates/visibility-apf/flowschema.yaml
+++ b/charts/kueue/templates/visibility/flowschema.yaml
@@ -1,4 +1,3 @@
-{{- if .Values.enableVisibilityAPF }}
 apiVersion: flowcontrol.apiserver.k8s.io/v1
 kind: FlowSchema
 metadata:
@@ -30,4 +29,3 @@ spec:
         - group:
             name: system:authenticated
           kind: Group
-{{- end }}
diff --git a/charts/kueue/templates/visibility-apf/prioritylevelconfigurations.yaml b/charts/kueue/templates/visibility/prioritylevelconfigurations.yaml
similarity index 90%
rename from charts/kueue/templates/visibility-apf/prioritylevelconfigurations.yaml
rename to charts/kueue/templates/visibility/prioritylevelconfigurations.yaml
index 1175cae2f..970c73dc6 100644
--- a/charts/kueue/templates/visibility-apf/prioritylevelconfigurations.yaml
+++ b/charts/kueue/templates/visibility/prioritylevelconfigurations.yaml
@@ -1,4 +1,3 @@
-{{- if .Values.enableVisibilityAPF }}
 apiVersion: flowcontrol.apiserver.k8s.io/v1
 kind: PriorityLevelConfiguration
 metadata:
@@ -17,4 +16,3 @@ spec:
       type: Queue
     nominalConcurrencyShares: 10
   type: Limited
-{{- end }}
diff --git a/charts/kueue/values.yaml b/charts/kueue/values.yaml
index 1a79a66b7..0ccd09fd8 100644
--- a/charts/kueue/values.yaml
+++ b/charts/kueue/values.yaml
@@ -7,8 +7,6 @@ fullnameOverride: ""
 enablePrometheus: false
 # Enable x509 automated certificate management using cert-manager (cert-manager.io)
 enableCertManager: false
-# Enable API Priority and Fairness configuration for the visibility API
-enableVisibilityAPF: false
 # Customize controllerManager
 controllerManager:
   #featureGates:
diff --git a/config/components/visibility-apf/kustomization.yaml b/config/components/visibility-apf/kustomization.yaml
deleted file mode 100644
index 40d982fc9..000000000
--- a/config/components/visibility-apf/kustomization.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-resources:
-- flowschema.yaml
-- prioritylevelconfigurations.yaml
diff --git a/config/components/visibility-apf/flowschema.yaml b/config/components/visibility/flowschema.yaml
similarity index 100%
rename from config/components/visibility-apf/flowschema.yaml
rename to config/components/visibility/flowschema.yaml
diff --git a/config/components/visibility/kustomization.yaml b/config/components/visibility/kustomization.yaml
index cd29a9935..23c787354 100644
--- a/config/components/visibility/kustomization.yaml
+++ b/config/components/visibility/kustomization.yaml
@@ -2,3 +2,5 @@ resources:
 - apiservice_v1beta1.yaml
 - role_binding.yaml
 - service.yaml
+- flowschema.yaml
+- prioritylevelconfigurations.yaml
diff --git a/config/components/visibility-apf/prioritylevelconfigurations.yaml b/config/components/visibility/prioritylevelconfigurations.yaml
similarity index 100%
rename from config/components/visibility-apf/prioritylevelconfigurations.yaml
rename to config/components/visibility/prioritylevelconfigurations.yaml
diff --git a/config/visibility-apf/kustomization.yaml b/config/visibility-apf/kustomization.yaml
deleted file mode 100644
index 847f776a9..000000000
--- a/config/visibility-apf/kustomization.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-# This overlay builds the APF components to be used in combination
-# with other overlays.
-
-namespace: kueue-system
-namePrefix: kueue-
-
-labels:
-  - pairs:
-      app.kubernetes.io/name: kueue
-      app.kubernetes.io/component: controller
-      control-plane: controller-manager
-
-resources:
-  - ../components/visibility-apf/
\ No newline at end of file
diff --git a/go.mod b/go.mod
index f737253a5..9c177b655 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	github.com/spf13/cobra v1.9.1
 	github.com/spf13/pflag v1.0.6
 	go.uber.org/zap v1.27.0
-	golang.org/x/sync v0.12.0
+	golang.org/x/sync v0.13.0
 	k8s.io/api v0.32.3
 	k8s.io/apimachinery v0.32.3
 	k8s.io/apiserver v0.32.3
@@ -36,7 +36,7 @@ require (
 	k8s.io/utils v0.0.0-20241210054802-24370beab758
 	sigs.k8s.io/controller-runtime v0.20.4
 	sigs.k8s.io/jobset v0.8.1
-	sigs.k8s.io/lws v0.6.0
+	sigs.k8s.io/lws v0.6.1
 	sigs.k8s.io/structured-merge-diff/v4 v4.7.0
 	sigs.k8s.io/yaml v1.4.0
 )
@@ -124,7 +124,7 @@ require (
 	golang.org/x/crypto v0.36.0 // indirect
 	golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
 	golang.org/x/mod v0.24.0 // indirect
-	golang.org/x/net v0.37.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
 	golang.org/x/oauth2 v0.28.0 // indirect
 	golang.org/x/sys v0.32.0 // indirect
 	golang.org/x/term v0.30.0 // indirect
diff --git a/go.sum b/go.sum
index 340afff0e..2e417ce6c 100644
--- a/go.sum
+++ b/go.sum
@@ -285,15 +285,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 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.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
-golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
 golang.org/x/oauth2 v0.28.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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
-golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.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=
@@ -393,8 +393,8 @@ sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo
 sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
 sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
 sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
-sigs.k8s.io/lws v0.6.0 h1:qHvhm54daBzL1S/+e8bafp7BKRC3jhrqY+EgpDGf41o=
-sigs.k8s.io/lws v0.6.0/go.mod h1:7nbwcpHwdDticuWPTDe6Va5OpjasS0MoVeVD61N5Y0c=
+sigs.k8s.io/lws v0.6.1 h1:cWiRmMSflo8hQPBrmIIZtoaX3XuVkmAgFKkmjxlPULI=
+sigs.k8s.io/lws v0.6.1/go.mod h1:aoT5ROMriBtN/H8JH0POBF6e2uyFCOxKGKtXSA3DVV8=
 sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
 sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
 sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
diff --git a/hack/update-helm.sh b/hack/update-helm.sh
index ff354d635..95b3cf14e 100755
--- a/hack/update-helm.sh
+++ b/hack/update-helm.sh
@@ -23,14 +23,12 @@ SRC_CRD_DIR=config/components/crd/bases
 SRC_RBAC_DIR=config/components/rbac
 SRC_WEBHOOK_DIR=config/components/webhook
 SRC_VISIBILITY_DIR=config/components/visibility
-SRC_VISIBILITY_APF_DIR=config/components/visibility-apf
 SRC_KUEUEVIZ_DIR=config/components/kueueviz
 
 DEST_CRD_DIR=charts/kueue/templates/crd
 DEST_RBAC_DIR=charts/kueue/templates/rbac
 DEST_WEBHOOK_DIR=charts/kueue/templates/webhook
 DEST_VISIBILITY_DIR=charts/kueue/templates/visibility
-DEST_VISIBILITY_APF_DIR=charts/kueue/templates/visibility-apf
 DEST_KUEUEVIZ_DIR=charts/kueue/templates/kueueviz
 
 YQ=./bin/yq
@@ -53,8 +51,6 @@ find $SRC_WEBHOOK_DIR -name "*.yaml" $EXCLUDE_FILES_ARGS -exec cp "{}" $DEST_WEB
 # shellcheck disable=SC2086
 find $SRC_VISIBILITY_DIR -name "*.yaml" $EXCLUDE_FILES_ARGS -exec cp "{}" $DEST_VISIBILITY_DIR \;
 # shellcheck disable=SC2086
-find $SRC_VISIBILITY_APF_DIR -name "*.yaml" $EXCLUDE_FILES_ARGS -exec cp "{}" $DEST_VISIBILITY_APF_DIR \;
-# shellcheck disable=SC2086
 find $SRC_KUEUEVIZ_DIR -name "*.yaml" $EXCLUDE_FILES_ARGS -exec cp "{}" $DEST_KUEUEVIZ_DIR \;
 $YQ -N -s '.kind' ${DEST_WEBHOOK_DIR}/manifests.yaml
 rm ${DEST_WEBHOOK_DIR}/manifests.yaml
@@ -413,19 +409,6 @@ EOT
   $SED -i '/^metadata:.*/a\  labels:\n  {{- include "kueue.labels" . | nindent 4 }}' "$output_file"
 done
 
-# Replace flowcontrol version on visibility-apf directory
-for output_file in "${DEST_VISIBILITY_APF_DIR}"/*.yaml; do
-  $YQ -N -i '.metadata.name |= "{{ include \"kueue.fullname\" . }}-" + .' "$output_file"
-  $YQ -N -i '.metadata.namespace = "{{ .Release.Namespace }}"' "$output_file"
-  $SED -i '/^metadata:.*/a\  labels:\n  {{- include "kueue.labels" . | nindent 4 }}' "$output_file"
-  {
-    echo '{{- if .Values.enableVisibilityAPF }}'
-    cat "$output_file"
-    echo "{{- end }}"
-  } > "${output_file}.tmp"
-  mv "${output_file}.tmp" "${output_file}"
-done
-
 # Add kueueviz templating on kueueviz directory
 for output_file in "${DEST_KUEUEVIZ_DIR}"/*.yaml; do
   if [ "$(< "$output_file" $YQ '.kind | select(. == "Deployment")')" ]; then
diff --git a/keps/4136-admission-fair-sharing/README.md b/keps/4136-admission-fair-sharing/README.md
new file mode 100644
index 000000000..6435d0f87
--- /dev/null
+++ b/keps/4136-admission-fair-sharing/README.md
@@ -0,0 +1,255 @@
+# KEP-4136: Admission Fair Sharing
+
+<!-- toc -->
+- [Summary](#summary)
+- [Motivation](#motivation)
+  - [Goals](#goals)
+  - [Non-Goals](#non-goals)
+- [Proposal](#proposal)
+  - [User Stories (Optional)](#user-stories-optional)
+    - [Story 1](#story-1)
+    - [Story 2](#story-2)
+  - [Risks and Mitigations](#risks-and-mitigations)
+- [Design Details](#design-details)
+  - [Test Plan](#test-plan)
+      - [Prerequisite testing updates](#prerequisite-testing-updates)
+    - [Unit Tests](#unit-tests)
+    - [Integration tests](#integration-tests)
+  - [Graduation Criteria](#graduation-criteria)
+- [Drawbacks](#drawbacks)
+- [Alternatives](#alternatives)
+<!-- /toc -->
+
+## Summary
+
+This KEP describes the mechanism for fair admission of workloads coming from a group of
+different sources (like Cluster and Local Queues) based on the source shared resource usage.
+Workloads from sources that use less are admitted before workloads coming from sources that use more. 
+
+## Motivation
+
+Currently Kueue has a Fair Sharing mechanism that enforces fair-sharing of unused resources
+via preemption. If one Cluster Queue is using much more of the resources than the other one
+that is in need, some workloads from the first one may be preempted to allow more “fair” distribution. 
+
+This model has multiple assumptions:
+
+* Workloads can be preempted.
+* Users get a proper quota to do their regular business.
+* Users come from multiple teams/organizations and they would rather have a strict policy but 
+fair policy than show understanding to those consuming all shared resources. 
+
+However, these assumptions are not universal. Sometimes:
+
+* Workloads should not be preempted.
+* Users operate on a shared but bigger quota.
+* Fairness should not win over getting the workloads eventually completed.
+
+In that case the existing mode doesn’t work and a different one needs to be employed.
+
+### Goals
+
+* Establish a method for how shared resource usage is calculated and recorded and how users can fine tune the mechanism.
+* Allow to specify a fair admission scope at either individual Cluster Queue or Cohort scope.
+* Allow to specify the relative importance of LocalQueues targeting the same ClusterQueue.
+* Amend the admission mechanism to work on admission scopes instead of only on ClusterQueues. 
+* Select the appropriate admission candidates for each of the admission scopes and admit them according to the selected queueing policy.
+* Make the new mechanism complementary to the existing preemption-based fair sharing
+
+### Non-Goals
+
+* Store time series data inside K8S.
+* Provide precise shared resource usage accounting or billing.
+
+## Proposal
+
+* Modify CQ’s FairSharing struct with 
+
+```go
+type FairSharing struct {
+	// Weight denotes how important the given queue when competing against other queues 
+// for unused shared resources. The exact impact of the weight in fair share calculations
+// depends on the fair share algorithm used. Default = 1.
+	Weight *resource.Quantity `json:"weight,omitempty"`
+}
+```
+
+* Expand LQ spec with the same FairSharing struct (Cohort will be expanded with FairSharing 
+as a part of the Preemptive FS implementation in hierarchical structure).
+
+* Modify FairSharingStatus struct with 
+
+```go
+type FairSharingStatus struct {
+	// WeightedShare represents the usage above nominal quota, with the weight applied in.
+	// The bigger the value is the more shared resources has been allocated and the less 
+	// entitled the queue is for more shared resources. 
+	// The exact details and the interpretation of the value depends on 
+    // the fair sharing algorithm used.
+	WeightedShare int64 `json:"weightedShare"`
+
+	
+	// ConsumedResources represents the aggregated usage of resources over time,
+	// with decaying function applied. 
+	// The value is populated if usage consumption functionality is enabled in Kueue config.
+	ConsumedResources corev1.ResourceList `json:"consumedResources,omitempty"`
+
+	// LastUpdate is the time when share and consumed resources were updated.
+	LastUpdate metav1.Time `json:"lastUpdate,omitempty"`
+}
+```
+
+* Add FairSharingStatus to LocalQueue (and Cohort).
+
+* Create a new struct AdmissionScope and make it an optional field for CQ and Cohort Spec. If
+not provided, CQ or Cohort is not considered an AdmissionScope and is not a subject for new
+admission logic. If there are two AdmissionScopes on the path from CQ/Cohort to the top of
+the hierarchy tree, the higher one is used.
+
+```go
+const (
+	// FairSharing based on usage, with QueuingStrategy as defined in CQ.
+	UsageBasedAdmissionFairSharing AdmissionMode = UsageBasedFairSharing
+
+    NoAdmissionFairSharing AdmissionMode = NoAdmissionFairSharing
+)
+
+type AdmissionScope struct {
+    AdmissionMode AdmissionMode
+}
+```
+
+* When selecting candidates for admission groups all workloads from LQ to CQ or the
+topmost Cohort that is marked with AdmissionScope. Then sort them using criterias:
+
+- Usage vector  built from ConsumedResources from TopCohort to LQ
+- Priority
+- Timestamp
+
+The usage vector will be sum of the `ConsumedResources` weighted according to `resourceWeights`
+(mentioned later in the KEP)
+
+Let’s look at a couple scenarios:
+
+1. AdmissionScope at CQ, CQ queueing policy is FIFO, 3 LQ pointing to CQ. Kueue considers
+all CQ resources and potentially borrowed resources as “shared” resources and fair sharing
+is applied to all workloads.
+Kueue sorts the workloads by their LQ usage (if mode is `UsageBasedFairSharing`), priority and
+timestamp and tries to admit the first one from the list. Other workloads are not attempted
+until the first one is not admitted.
+
+2.  AdmissionScope at CQ, CQ queueing policy is BestEffort, 3 LQ pointing to CQ. 
+Kueue considers all CQ resources and potentially borrowed resources as “shared” resources and
+fair sharing is applied to all workloads.
+
+Kueue sorts the workloads by their LQ usage (if mode is `UsageBasedFairSharing`), priority and
+timestamp and tries to admit the first one from the list. If it fails and the second, third
+or following is possible then that workload is admitted, under condition that it might get preempted.
+
+3. AdmissionScope at Cohort level - Kueue operates in a mixed mode. Inside CQ workloads are
+selected according to their AdmissionMode (if specified). If a workload fits entirely into
+nominal quota, then it is admitted immediately, if not it goes into cohort-level fair sharing.
+For Cohort we select all the “sticking out” workloads, and sort them by their CQ usage, priority
+and timestamp. Kueue attempts to admit the first workload from the list of sticking-out
+(workload + current_usage > resources), just like if it was one big strict FIFO queue. 
+For multi-level hierarchy under one AdmissionScope we would treat underlying Cohorts as Fifo CQs.
+
+
+* Additionally there will be the resource usage calculation loop. The frequency of the
+calculation will be controlled globally in Kueue’s config file. Accounting would be done
+using something like geometric average:
+
+usage_sum = (1-A) * previous_usage_sum + A * current_usage.  
+
+The value will be stored in FairSharingStatus for all LQ, CQ, and Cohorts. The value will not be zeroed
+after Kueue restart or after brief period of downtime. However if the period is longer,
+the value should be automatically zeroed.
+
+
+The user will be able to configure the decaying factor A in Kueue’s config file by specifying
+the half life decay time - after what time the current shared usage will decay to half of its original value.
+
+A = 1 - 0.5 ^ (sampling/half_life_decay)
+
+* Configuration will sit in FairSharing stuct in Kueue config. There will be the following modifications:
+
+  - usageHalfLifeDecayTime - half life decay time of usage, as described above.
+  - usageSamplingInterval - how often usage is calculated.
+  - resourceWeights - how much consumption of individual resources is important when comparing usage.
+  - resetInactivityPeriod - if Kueue has not updated the value for this period then the value should be zeroed.
+ 
+If the user doesn't want any preemptions while fair sharing, preemptionStrategies should be left empty.
+
+* If preemptionStrategies is non empty Kueue attempts to combine two fair sharings at the same time. 
+For each of the Admission Scopes Kueue selects one workload to be attempted. And then these pre-selected 
+workloads are sorted based on their Preemption-based fair share value. If some of them don't fit,
+fair sharing preemption may be executed. So admission-based fair sharing only reshuffles workloads
+within AdmissionScope and then other mechanisms are applied as usual.
+
+### User Stories (Optional)
+#### Story 1
+
+I have multiple users using the same ClusterQueue. Each has its own namespace and LocalQueue
+through which they submit workloads. I want to fairly admit their workloads so that one active
+user doesn’t block the cluster too much.
+
+#### Story 2
+
+I have multiple teams that may be sending workloads of various sizes. I want to give each team
+some guaranteed capacity and at the same time, allow them to fairly share some bigger pool of resources. 
+
+### Risks and Mitigations
+
+* Having 2 fair sharing mechanisms and confusion between preemption-based fair sharing and admission time fair sharing.
+
+* Increased complexity of the project.
+
+## Design Details
+
+Covered in Proposal.
+
+### Test Plan
+[x] I/we understand the owners of the involved components may require updates to
+existing tests to make this code solid enough prior to committing the changes necessary
+to implement this enhancement.
+##### Prerequisite testing updates
+
+#### Unit Tests
+The code will be thoroughly covered with unit tests.
+
+In particular:
+* CQ level fair sharing between LQ for both strict FIFO and best effort.
+* Cohort with owned resources, and CQ with guaranteed quota.
+
+
+#### Integration tests
+
+They will mainly focus on larger scope scheduling (involving multiple cohorts/cqs) and
+interactions with preemptive fair sharing outside admission scope.
+
+### Graduation Criteria
+
+The implementation will be split into 2 subfeatures:
+
+* CQ + LQ level support
+* Multi-level Cohort+CQ+LQ support
+
+Obviously, the second depends on the first to some extent. The first however may reach
+Beta/GA without starting the second.
+
+The graduation criterias are quite standard:
+
+* Beta - positive feedback from Alpha, api seems reasonable.
+* GA - positive feedback, no bugs, no api changes needed.
+
+We hope to have CQ+LQ in alpha for the next Kueue release (0.12).
+
+## Drawbacks
+* Adds additional complexity to the system.
+* Creates yet another fair sharing mechanism.
+
+## Alternatives
+
+* Not having the feature.
+* Modifying/replacing the existing preemptive fair sharing algorithm.
+
diff --git a/keps/4136-admission-fair-sharing/kep.yaml b/keps/4136-admission-fair-sharing/kep.yaml
new file mode 100644
index 000000000..a06a2e831
--- /dev/null
+++ b/keps/4136-admission-fair-sharing/kep.yaml
@@ -0,0 +1,36 @@
+title: Admission fair sharing
+kep-number: 4136
+authors:
+  - "@mwielgus"
+status: draft
+creation-date: 2025-02-03
+reviewers:
+  - "@mimowo"
+  - "@gabesaba"
+  - "@pbundyra"
+approvers:
+  - "@mimowo"
+  - "@tenzen-y"
+sea-also:
+  - "KEP-1714"
+
+# The target maturity stage in the current dev cycle for this KEP.
+stage: alpha
+
+# The most recent milestone for which work toward delivery of this KEP has been
+# done. This can be the current (upcoming) milestone, if it is being actively
+# worked on.
+latest-milestone: "v0.12"
+
+# The milestone at which this feature was, or is targeted to be, at each stage.
+milestone:
+  alpha: "v0.12"
+
+# The following PRR answers are required at alpha release
+# List the feature gate name and the components for which it must be enabled
+disable-supported: true
+
+# The following PRR answers are required at beta release
+# metrics:
+#   - my_feature_metric
+
diff --git a/pkg/cache/cohort_snapshot.go b/pkg/cache/cohort_snapshot.go
index 5dd7c8bf1..bf74056e6 100644
--- a/pkg/cache/cohort_snapshot.go
+++ b/pkg/cache/cohort_snapshot.go
@@ -21,6 +21,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/hierarchy"
+	"sigs.k8s.io/kueue/pkg/resources"
 )
 
 type CohortSnapshot struct {
@@ -88,3 +89,7 @@ func (c *CohortSnapshot) parentHRN() hierarchicalResourceNode {
 func (c *CohortSnapshot) fairWeight() *resource.Quantity {
 	return &c.FairWeight
 }
+
+func (c *CohortSnapshot) BorrowingWith(fr resources.FlavorResource, val int64) bool {
+	return c.ResourceNode.Usage[fr]+val > c.ResourceNode.SubtreeQuota[fr]
+}
diff --git a/pkg/cache/resource_node.go b/pkg/cache/resource_node.go
index 650205116..9d4062798 100644
--- a/pkg/cache/resource_node.go
+++ b/pkg/cache/resource_node.go
@@ -19,6 +19,8 @@ package cache
 import (
 	"maps"
 
+	"k8s.io/apimachinery/pkg/util/sets"
+
 	"sigs.k8s.io/kueue/pkg/hierarchy"
 	"sigs.k8s.io/kueue/pkg/resources"
 )
@@ -77,6 +79,13 @@ type hierarchicalResourceNode interface {
 	parentHRN() hierarchicalResourceNode
 }
 
+// LocalAvailable returns, for a given node and resource flavor,
+// how much guaranteed quota in this flavor exceeds usage.
+// This quota is available at this node but is not visible at its parent.
+func LocalAvailable(node hierarchicalResourceNode, fr resources.FlavorResource) int64 {
+	return max(0, node.getResourceNode().guaranteedQuota(fr)-node.getResourceNode().Usage[fr])
+}
+
 // available determines how much capacity remains for the current
 // node, taking into account usage and BorrowingLimits. It finds remaining
 // capacity which is stored locally. If the node has a parent, it
@@ -91,7 +100,6 @@ func available(node hierarchicalResourceNode, fr resources.FlavorResource) int64
 	if !node.HasParent() {
 		return r.SubtreeQuota[fr] - r.Usage[fr]
 	}
-	localAvailable := max(0, r.guaranteedQuota(fr)-r.Usage[fr])
 	parentAvailable := available(node.parentHRN(), fr)
 
 	if borrowingLimit := r.Quotas[fr].BorrowingLimit; borrowingLimit != nil {
@@ -100,7 +108,7 @@ func available(node hierarchicalResourceNode, fr resources.FlavorResource) int64
 		withMaxFromParent := storedInParent - usedInParent + *borrowingLimit
 		parentAvailable = min(withMaxFromParent, parentAvailable)
 	}
-	return localAvailable + parentAvailable
+	return LocalAvailable(node, fr) + parentAvailable
 }
 
 // potentialAvailable returns the maximum capacity available to this node,
@@ -122,7 +130,7 @@ func potentialAvailable(node hierarchicalResourceNode, fr resources.FlavorResour
 // its Cohort when usage exceeds guaranteedQuota.
 func addUsage(node hierarchicalResourceNode, fr resources.FlavorResource, val int64) {
 	r := node.getResourceNode()
-	localAvailable := max(0, r.guaranteedQuota(fr)-r.Usage[fr])
+	localAvailable := LocalAvailable(node, fr)
 	r.Usage[fr] += val
 	if node.HasParent() && val > localAvailable {
 		deltaParentUsage := val - localAvailable
@@ -191,3 +199,30 @@ func accumulateFromChild(parent *cohort, child hierarchicalResourceNode) {
 		parent.resourceNode.Usage[fr] += max(0, childUsage-child.getResourceNode().guaranteedQuota(fr))
 	}
 }
+
+// QuantitiesFitInQuota returns if resource requests fit in quota on this node
+// and the amount of resources that exceed the guaranteed
+// quota on this node. It is assumed that subsequent call
+// to this function will be on nodes parent with remainingRequests
+func QuantitiesFitInQuota(node hierarchicalResourceNode, requests resources.FlavorResourceQuantities) (bool, resources.FlavorResourceQuantities) {
+	fits := true
+	remainingRequests := make(resources.FlavorResourceQuantities, len(requests))
+	for fr, v := range requests {
+		if node.getResourceNode().Usage[fr]+v > node.getResourceNode().SubtreeQuota[fr] {
+			fits = false
+		}
+		remainingRequests[fr] = max(0, v-LocalAvailable(node, fr))
+	}
+	return fits, remainingRequests
+}
+
+// IsWithinNominalInResources returns whether or not, the node quota usage exceeds its
+// nominal quota in any resource flavor out of a set of resource flavours.
+func IsWithinNominalInResources(node hierarchicalResourceNode, frs sets.Set[resources.FlavorResource]) bool {
+	for fr := range frs {
+		if node.getResourceNode().Usage[fr] > node.getResourceNode().SubtreeQuota[fr] {
+			return false
+		}
+	}
+	return true
+}
diff --git a/pkg/cache/tas_flavor_snapshot.go b/pkg/cache/tas_flavor_snapshot.go
index f525bb33b..4fbde49c7 100644
--- a/pkg/cache/tas_flavor_snapshot.go
+++ b/pkg/cache/tas_flavor_snapshot.go
@@ -26,6 +26,7 @@ import (
 
 	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	corev1helpers "k8s.io/component-helpers/scheduling/corev1"
 	"k8s.io/utils/ptr"
 
@@ -83,6 +84,10 @@ type leafDomain struct {
 	// nodeTaints contains the list of taints for the node, only applies for
 	// lowest level of topology, if the lowest level is node
 	nodeTaints []corev1.Taint
+
+	// nodeLabels contains the list of labels on the node, only applies for
+	// lowest level of topology, if the lowest level is node
+	nodeLabels map[string]string
 }
 
 type domainByID map[utiltas.TopologyDomainID]*domain
@@ -150,6 +155,7 @@ func (s *TASFlavorSnapshot) addNode(node corev1.Node) utiltas.TopologyDomainID {
 		}
 		if s.isLowestLevelNode() {
 			leafDomain.nodeTaints = slices.Clone(node.Spec.Taints)
+			leafDomain.nodeLabels = node.GetLabels()
 		}
 		s.leaves[domainID] = &leafDomain
 	}
@@ -410,6 +416,7 @@ func (s *TASFlavorSnapshot) findTopologyAssignment(
 	requests := tasPodSetRequests.SinglePodRequests.Clone()
 	requests.Add(resources.Requests{corev1.ResourcePods: 1})
 	podSetTolerations := tasPodSetRequests.PodSet.Template.Spec.Tolerations
+	podSetNodeSelectors := tasPodSetRequests.PodSet.Template.Spec.NodeSelector
 	count := tasPodSetRequests.Count
 	required := isRequired(tasPodSetRequests.PodSet.TopologyRequest)
 	key := s.levelKeyWithImpliedFallback(&tasPodSetRequests)
@@ -421,8 +428,24 @@ func (s *TASFlavorSnapshot) findTopologyAssignment(
 	if !found {
 		return nil, fmt.Sprintf("no requested topology level: %s", *key)
 	}
+	var selector labels.Selector
+	if s.isLowestLevelNode() {
+		sel, err := labels.ValidatedSelectorFromSet(podSetNodeSelectors)
+		if err != nil {
+			return nil, fmt.Sprintf("invalid node selectors: %s, reason: %s", podSetNodeSelectors, err)
+		}
+		selector = sel
+	} else {
+		selector = labels.Everything()
+	}
 	// phase 1 - determine the number of pods which can fit in each topology domain
-	s.fillInCounts(requests, assumedUsage, simulateEmpty, append(podSetTolerations, s.tolerations...))
+	s.fillInCounts(
+		requests,
+		assumedUsage,
+		simulateEmpty,
+		append(podSetTolerations, s.tolerations...),
+		selector,
+	)
 
 	// phase 2a: determine the level at which the assignment is done along with
 	// the domains which can accommodate all pods
@@ -647,13 +670,15 @@ func (s *TASFlavorSnapshot) sortedDomains(domains []*domain, unconstrained bool)
 func (s *TASFlavorSnapshot) fillInCounts(requests resources.Requests,
 	assumedUsage map[utiltas.TopologyDomainID]resources.Requests,
 	simulateEmpty bool,
-	tolerations []corev1.Toleration) {
+	tolerations []corev1.Toleration,
+	selector labels.Selector) {
 	for _, domain := range s.domains {
 		// cleanup the state in case some remaining values are present from computing
 		// assignments for previous PodSets.
 		domain.state = 0
 	}
 	for _, leaf := range s.leaves {
+		// 1. Check Tolerations against Node Taints
 		taint, untolerated := corev1helpers.FindMatchingUntoleratedTaint(leaf.nodeTaints, tolerations, func(t *corev1.Taint) bool {
 			return t.Effect == corev1.TaintEffectNoSchedule || t.Effect == corev1.TaintEffectNoExecute
 		})
@@ -661,6 +686,17 @@ func (s *TASFlavorSnapshot) fillInCounts(requests resources.Requests,
 			s.log.V(2).Info("excluding node with untolerated taint", "domainID", leaf.id, "taint", taint)
 			continue
 		}
+		// 2. Check Node Labels against Compiled Selector
+		var nodeLabelSet labels.Set
+		if leaf.nodeLabels != nil {
+			nodeLabelSet = leaf.nodeLabels
+		}
+		// isLowestLevelNode() is necessary because we gather node level information only when
+		// node is the lowest level of the topology
+		if s.isLowestLevelNode() && !selector.Matches(nodeLabelSet) {
+			s.log.V(2).Info("excluding node that doesn't match nodeSelectors", "domainID", leaf.id, "nodeLabels", nodeLabelSet)
+			continue
+		}
 		remainingCapacity := leaf.freeCapacity.Clone()
 		if !simulateEmpty {
 			remainingCapacity.Sub(leaf.tasUsage)
diff --git a/pkg/controller/admissionchecks/provisioning/controller.go b/pkg/controller/admissionchecks/provisioning/controller.go
index 7dfa172db..e6d2811d6 100644
--- a/pkg/controller/admissionchecks/provisioning/controller.go
+++ b/pkg/controller/admissionchecks/provisioning/controller.go
@@ -23,7 +23,6 @@ import (
 	"errors"
 	"fmt"
 	"maps"
-	"time"
 
 	corev1 "k8s.io/api/core/v1"
 	apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -45,7 +44,6 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/constants"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/podset"
 	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
 	"sigs.k8s.io/kueue/pkg/util/api"
@@ -164,16 +162,13 @@ func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		return reconcile.Result{}, err
 	}
 
-	requeueAfter, err := c.syncOwnedProvisionRequest(ctx, wl, &wlInfo, checkConfig, activeOrLastPRForChecks)
+	err = c.syncOwnedProvisionRequest(ctx, wl, &wlInfo, checkConfig, activeOrLastPRForChecks)
 	if err != nil {
 		// this can also delete unneeded checks
 		log.V(2).Error(err, "syncOwnedProvisionRequest failed")
 		return reconcile.Result{}, err
 	}
 
-	if requeueAfter != nil {
-		return reconcile.Result{RequeueAfter: *requeueAfter}, nil
-	}
 	return reconcile.Result{}, nil
 }
 
@@ -229,9 +224,8 @@ func (c *Controller) syncOwnedProvisionRequest(
 	wlInfo *workloadInfo,
 	checkConfig map[kueue.AdmissionCheckReference]*kueue.ProvisioningRequestConfig,
 	activeOrLastPRForChecks map[kueue.AdmissionCheckReference]*autoscaling.ProvisioningRequest,
-) (*time.Duration, error) {
+) error {
 	log := ctrl.LoggerFrom(ctx)
-	var requeAfter *time.Duration
 	for checkName, prc := range checkConfig {
 		if prc == nil {
 			// the check is not active
@@ -255,18 +249,8 @@ func (c *Controller) syncOwnedProvisionRequest(
 				ac != nil && ac.State == kueue.CheckStatePending {
 				// if the workload is in Retry/Rejected state we don't create another ProvReq
 				attempt = getAttempt(log, req, wl.Name, checkName)
-				if features.Enabled(features.KeepQuotaForProvReqRetry) {
-					remainingTime := c.remainingTimeToRetry(req, attempt, prc)
-					if remainingTime <= 0 {
-						shouldCreatePr = true
-						attempt++
-					} else if requeAfter == nil || remainingTime < *requeAfter {
-						requeAfter = &remainingTime
-					}
-				} else {
-					shouldCreatePr = true
-					attempt++
-				}
+				shouldCreatePr = true
+				attempt++
 			}
 		} else {
 			shouldCreatePr = true
@@ -296,7 +280,7 @@ func (c *Controller) syncOwnedProvisionRequest(
 				ps, psFound := podSetMap[psName]
 				psa, psaFound := psaMap[psName]
 				if !psFound || !psaFound {
-					return nil, errInconsistentPodSetAssignments
+					return errInconsistentPodSetAssignments
 				}
 
 				ptName := getProvisioningRequestPodTemplateName(requestName, psName)
@@ -304,14 +288,14 @@ func (c *Controller) syncOwnedProvisionRequest(
 				pt := &corev1.PodTemplate{}
 				err := c.client.Get(ctx, types.NamespacedName{Namespace: wl.Namespace, Name: ptName}, pt)
 				if client.IgnoreNotFound(err) != nil {
-					return nil, err
+					return err
 				}
 				if err != nil {
 					// it's a not found, so create it
 					_, err := c.createPodTemplate(ctx, wl, ptName, ps, psa)
 					if err != nil {
 						msg := fmt.Sprintf("Error creating PodTemplate %q: %v", ptName, err)
-						return nil, c.handleError(ctx, wl, ac, msg, err)
+						return c.handleError(ctx, wl, ac, msg, err)
 					}
 				}
 
@@ -324,41 +308,21 @@ func (c *Controller) syncOwnedProvisionRequest(
 			}
 
 			if err := ctrl.SetControllerReference(wl, req, c.client.Scheme()); err != nil {
-				return nil, err
+				return err
 			}
 
 			if err := c.client.Create(ctx, req); err != nil {
 				msg := fmt.Sprintf("Error creating ProvisioningRequest %q: %v", requestName, err)
-				return nil, c.handleError(ctx, wl, ac, msg, err)
+				return c.handleError(ctx, wl, ac, msg, err)
 			}
 			c.record.Eventf(wl, corev1.EventTypeNormal, "ProvisioningRequestCreated", "Created ProvisioningRequest: %q", req.Name)
 			activeOrLastPRForChecks[checkName] = req
 		}
 		if err := c.syncProvisionRequestsPodTemplates(ctx, wl, req); err != nil {
-			return nil, err
-		}
-	}
-	return requeAfter, nil
-}
-
-func (c *Controller) remainingTimeToRetry(pr *autoscaling.ProvisioningRequest, failuresCount int32, prc *kueue.ProvisioningRequestConfig) time.Duration {
-	backoffDuration := time.Duration(*prc.Spec.RetryStrategy.BackoffBaseSeconds) * time.Second
-	maxBackoffDuration := time.Duration(*prc.Spec.RetryStrategy.BackoffMaxSeconds) * time.Second
-	var cond *metav1.Condition
-	if isFailed(pr) {
-		cond = apimeta.FindStatusCondition(pr.Status.Conditions, autoscaling.Failed)
-	} else {
-		cond = apimeta.FindStatusCondition(pr.Status.Conditions, autoscaling.BookingExpired)
-	}
-	for i := 1; i < int(failuresCount); i++ {
-		backoffDuration *= 2
-		if backoffDuration >= maxBackoffDuration {
-			backoffDuration = maxBackoffDuration
-			break
+			return err
 		}
 	}
-	timeElapsedSinceLastFailure := time.Since(cond.LastTransitionTime.Time)
-	return backoffDuration - timeElapsedSinceLastFailure
+	return nil
 }
 
 func (c *Controller) handleError(ctx context.Context, wl *kueue.Workload, ac *kueue.AdmissionCheckState, msg string, err error) error {
@@ -574,9 +538,7 @@ func (c *Controller) syncCheckStates(
 					// it is going to be retried
 					message := fmt.Sprintf("Retrying after failure: %s", apimeta.FindStatusCondition(pr.Status.Conditions, autoscaling.Failed).Message)
 					updated = updateCheckMessage(&checkState, message) || updated
-					if features.Enabled(features.KeepQuotaForProvReqRetry) {
-						updated = updateCheckState(&checkState, kueue.CheckStatePending) || updated
-					} else if wl.Status.RequeueState == nil || getAttempt(log, pr, wl.Name, check) > ptr.Deref(wl.Status.RequeueState.Count, 0) {
+					if wl.Status.RequeueState == nil || getAttempt(log, pr, wl.Name, check) > ptr.Deref(wl.Status.RequeueState.Count, 0) {
 						// We don't want to Retry on old ProvisioningRequests
 						updated = true
 						updateCheckState(&checkState, kueue.CheckStateRetry)
@@ -601,9 +563,7 @@ func (c *Controller) syncCheckStates(
 						// it is going to be retried
 						message := fmt.Sprintf("Retrying after booking expired: %s", apimeta.FindStatusCondition(pr.Status.Conditions, autoscaling.BookingExpired).Message)
 						updated = updateCheckMessage(&checkState, message) || updated
-						if features.Enabled(features.KeepQuotaForProvReqRetry) {
-							updated = updateCheckState(&checkState, kueue.CheckStatePending) || updated
-						} else if wl.Status.RequeueState == nil || getAttempt(log, pr, wl.Name, check) > ptr.Deref(wl.Status.RequeueState.Count, 0) {
+						if wl.Status.RequeueState == nil || getAttempt(log, pr, wl.Name, check) > ptr.Deref(wl.Status.RequeueState.Count, 0) {
 							updated = true
 							updateCheckState(&checkState, kueue.CheckStateRetry)
 							workload.UpdateRequeueState(wlPatch, backoffBaseSeconds, backoffMaxSeconds, c.clock)
diff --git a/pkg/controller/core/workload_controller.go b/pkg/controller/core/workload_controller.go
index 990edb238..b7f59f9d5 100644
--- a/pkg/controller/core/workload_controller.go
+++ b/pkg/controller/core/workload_controller.go
@@ -168,7 +168,7 @@ func (r *WorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
 		var updated bool
 		if cond := apimeta.FindStatusCondition(wl.Status.Conditions, kueue.WorkloadRequeued); cond != nil && cond.Status == metav1.ConditionFalse {
 			switch cond.Reason {
-			case kueue.WorkloadDeactivated, kueue.WorkloadEvictedByDeactivation:
+			case kueue.WorkloadDeactivated:
 				workload.SetRequeuedCondition(&wl, kueue.WorkloadReactivated, "The workload was reactivated", true)
 				updated = true
 			case kueue.WorkloadEvictedByPodsReadyTimeout, kueue.WorkloadEvictedByAdmissionCheck:
diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go
index 4112695fc..2c9ca6c1b 100644
--- a/pkg/features/kube_features.go
+++ b/pkg/features/kube_features.go
@@ -116,12 +116,6 @@ const (
 	// all currently available ResourceFlavors for the LocalQueue.
 	ExposeFlavorsInLocalQueue featuregate.Feature = "ExposeFlavorsInLocalQueue"
 
-	// owner: @pbundyra
-	// kep: https://github.com/kubernetes-sigs/kueue/tree/main/keps/1136-provisioning-request-support
-	//
-	// Workloads keeps allocated quota and preserves QuotaReserved=True when ProvisioningRequest fails
-	KeepQuotaForProvReqRetry featuregate.Feature = "KeepQuotaForProvReqRetry"
-
 	// owner: @dgrove-oss
 	// kep: https://github.com/kubernetes-sigs/kueue/tree/main/keps/3589-manage-jobs-selectively
 	//
@@ -228,9 +222,6 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned
 	ExposeFlavorsInLocalQueue: {
 		{Version: version.MustParse("0.9"), Default: true, PreRelease: featuregate.Beta},
 	},
-	KeepQuotaForProvReqRetry: {
-		{Version: version.MustParse("0.9"), Default: false, PreRelease: featuregate.Deprecated},
-	},
 	ManagedJobsNamespaceSelector: {
 		{Version: version.MustParse("0.10"), Default: true, PreRelease: featuregate.Beta},
 	},
diff --git a/pkg/scheduler/flavorassigner/flavorassigner.go b/pkg/scheduler/flavorassigner/flavorassigner.go
index 5fd15714e..0000fd4ce 100644
--- a/pkg/scheduler/flavorassigner/flavorassigner.go
+++ b/pkg/scheduler/flavorassigner/flavorassigner.go
@@ -37,12 +37,15 @@ import (
 	"sigs.k8s.io/kueue/pkg/cache"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/resources"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/classical"
 	"sigs.k8s.io/kueue/pkg/workload"
 )
 
 type Assignment struct {
-	PodSets   []PodSetAssignment
-	Borrowing bool
+	PodSets []PodSetAssignment
+	// Borrowing is the height of the smallest cohort tree that fits
+	// the additional Usage. It equals to 0 if no borrowing is required.
+	Borrowing int
 	LastState workload.AssignmentClusterQueueState
 
 	// Usage is the accumulated Usage of resources as pod sets get
@@ -86,7 +89,7 @@ func (a *Assignment) computeTASUsage() workload.TASUsage {
 }
 
 // Borrows return whether assignment requires borrowing.
-func (a *Assignment) Borrows() bool {
+func (a *Assignment) Borrows() int {
 	return a.Borrowing
 }
 
@@ -331,7 +334,7 @@ type FlavorAssignment struct {
 	Name           kueue.ResourceFlavorReference
 	Mode           FlavorAssignmentMode
 	TriedFlavorIdx int
-	borrow         bool
+	borrow         int
 }
 
 type preemptionOracle interface {
@@ -481,8 +484,8 @@ func (a *Assignment) append(requests resources.Requests, psAssignment *PodSetAss
 	flavorIdx := make(map[corev1.ResourceName]int, len(psAssignment.Flavors))
 	a.PodSets = append(a.PodSets, *psAssignment)
 	for resource, flvAssignment := range psAssignment.Flavors {
-		if flvAssignment.borrow {
-			a.Borrowing = true
+		if flvAssignment.borrow > a.Borrowing {
+			a.Borrowing = flvAssignment.borrow
 		}
 		fr := resources.FlavorResource{Flavor: flvAssignment.Name, Resource: resource}
 		a.Usage.Quota[fr] += requests[resource]
@@ -568,7 +571,7 @@ func (a *FlavorAssigner) findFlavorForPodSetResource(
 			if mode < representativeMode {
 				representativeMode = mode
 			}
-			needsBorrowing = needsBorrowing || borrow
+			needsBorrowing = needsBorrowing || (borrow > 0)
 			if representativeMode == noFit {
 				// The flavor doesn't fit, no need to check other resources.
 				break
@@ -689,10 +692,9 @@ func flavorSelector(spec *corev1.PodSpec, allowedKeys sets.Set[string]) nodeaffi
 // if borrowing is required when preempting.
 // If the flavor doesn't satisfy limits immediately (when waiting or preemption
 // could help), it returns a Status with reasons.
-func (a *FlavorAssigner) fitsResourceQuota(log logr.Logger, fr resources.FlavorResource, val int64, rQuota cache.ResourceQuota) (granularMode, bool, *Status) {
+func (a *FlavorAssigner) fitsResourceQuota(log logr.Logger, fr resources.FlavorResource, val int64, rQuota cache.ResourceQuota) (granularMode, int, *Status) {
 	var status Status
 
-	borrow := a.cq.BorrowingWith(fr, val) && a.cq.HasParent()
 	available := a.cq.Available(fr)
 	maxCapacity := a.cq.PotentialAvailable(fr)
 
@@ -700,9 +702,10 @@ func (a *FlavorAssigner) fitsResourceQuota(log logr.Logger, fr resources.FlavorR
 	if val > maxCapacity {
 		status.appendf("insufficient quota for %s in flavor %s, request > maximum capacity (%s > %s)",
 			fr.Resource, fr.Flavor, resources.ResourceQuantityString(fr.Resource, val), resources.ResourceQuantityString(fr.Resource, maxCapacity))
-		return noFit, false, &status
+		return noFit, 0, &status
 	}
 
+	borrow, mayReclaimInHierarchy := classical.FindHeightOfLowestSubtreeThatFits(a.cq, fr, val)
 	// Fit
 	if val <= available {
 		return fit, borrow, nil
@@ -710,7 +713,8 @@ func (a *FlavorAssigner) fitsResourceQuota(log logr.Logger, fr resources.FlavorR
 
 	// Check if preemption is possible
 	mode := noFit
-	if val <= rQuota.Nominal {
+	// For single-level hierarchies, mayReclaimInHierarchy = true iff val <= rQuota.Nominal
+	if val <= rQuota.Nominal || mayReclaimInHierarchy {
 		mode = preempt
 		if a.oracle.IsReclaimPossible(log, a.cq, *a.wl, fr, val) {
 			mode = reclaim
diff --git a/pkg/scheduler/preemption/classical/candidate_generator.go b/pkg/scheduler/preemption/classical/candidate_generator.go
new file mode 100644
index 000000000..c782a5520
--- /dev/null
+++ b/pkg/scheduler/preemption/classical/candidate_generator.go
@@ -0,0 +1,160 @@
+/*
+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 classical
+
+import (
+	"sort"
+	"time"
+
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/utils/clock"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	"sigs.k8s.io/kueue/pkg/cache"
+	"sigs.k8s.io/kueue/pkg/resources"
+	"sigs.k8s.io/kueue/pkg/workload"
+)
+
+type candidateIterator struct {
+	candidates                        []*candidateElem
+	runIndex                          int
+	frsNeedPreemption                 sets.Set[resources.FlavorResource]
+	snapshot                          *cache.Snapshot
+	NoCandidateFromOtherQueues        bool
+	NoCandidateForHierarchicalReclaim bool
+	hierarchicalReclaimCtx            *HierarchicalPreemptionCtx
+}
+
+type candidateElem struct {
+	wl *workload.Info
+	// lca of this queue and cq (queue to which the new workload is submitted)
+	lca *cache.CohortSnapshot
+	// candidates above priority threshold cannot be preempted if at the same time
+	// cq would borrow from other queues/cohorts
+	preemptionVariant preemptionVariant
+}
+
+// Need a separate function for candidateElem data type
+// in the future we will modify the ordering to take into
+// account the distance in the cohort hierarchy tree
+func candidateElemsOrdering(candidates []*candidateElem, cq kueue.ClusterQueueReference, now time.Time, ordering func([]*workload.Info, kueue.ClusterQueueReference, time.Time) func(int, int) bool) func(int, int) bool {
+	// Adapt the candidatesOrdering function to work with candidateElem
+	adaptedOrdering := func(i, j int) bool {
+		a := candidates[i].wl
+		b := candidates[j].wl
+		return ordering([]*workload.Info{a, b}, cq, now)(0, 1)
+	}
+	return adaptedOrdering
+}
+
+func WorkloadUsesResources(wl *workload.Info, frsNeedPreemption sets.Set[resources.FlavorResource]) bool {
+	for _, ps := range wl.TotalRequests {
+		for res, flv := range ps.Flavors {
+			if frsNeedPreemption.Has(resources.FlavorResource{Flavor: flv, Resource: res}) {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// assume a prefix of the elements has condition WorkloadEvicted = true
+func splitEvicted(workloads []*candidateElem) ([]*candidateElem, []*candidateElem) {
+	firstFalse := sort.Search(len(workloads), func(i int) bool {
+		return !meta.IsStatusConditionTrue(workloads[i].wl.Obj.Status.Conditions, kueue.WorkloadEvicted)
+	})
+	return workloads[:firstFalse], workloads[firstFalse:]
+}
+
+// NewCandidateIterator creates a new iterator that yields candidate workloads for preemption
+// The iterator can be used to perform two independent runs over the list of candidates:
+// with and without borrowing. The runs are independent which means that the same candidates
+// might be returned for both, but note that the candidates with borrrowing are a subset of
+// candidates without borrowing.
+func NewCandidateIterator(hierarchicalReclaimCtx *HierarchicalPreemptionCtx, frsNeedPreemption sets.Set[resources.FlavorResource], snapshot *cache.Snapshot, clock clock.Clock, ordering func([]*workload.Info, kueue.ClusterQueueReference, time.Time) func(int, int) bool) *candidateIterator {
+	sameQueueCandidates := collectSameQueueCandidates(hierarchicalReclaimCtx)
+	hierarchyCandidates, priorityCandidates := collectCandidatesForHierarchicalReclaim(hierarchicalReclaimCtx)
+	sort.Slice(sameQueueCandidates, candidateElemsOrdering(sameQueueCandidates, hierarchicalReclaimCtx.Cq.Name, clock.Now(), ordering))
+	sort.Slice(priorityCandidates, candidateElemsOrdering(priorityCandidates, hierarchicalReclaimCtx.Cq.Name, clock.Now(), ordering))
+	sort.Slice(hierarchyCandidates, candidateElemsOrdering(hierarchyCandidates, hierarchicalReclaimCtx.Cq.Name, clock.Now(), ordering))
+	evictedHierarchicalReclaimCandidates, nonEvictedHierarchicalReclaimCandidates := splitEvicted(hierarchyCandidates)
+	evictedSTCandidates, nonEvictedSTCandidates := splitEvicted(priorityCandidates)
+	evictedSameQueueCandidates, nonEvictedSameQueueCandidates := splitEvicted(sameQueueCandidates)
+	allCandidates := make([]*candidateElem, 0, len(hierarchyCandidates)+len(priorityCandidates)+len(sameQueueCandidates))
+	allCandidates = append(allCandidates, evictedHierarchicalReclaimCandidates...)
+	allCandidates = append(allCandidates, evictedSTCandidates...)
+	allCandidates = append(allCandidates, evictedSameQueueCandidates...)
+	allCandidates = append(allCandidates, nonEvictedHierarchicalReclaimCandidates...)
+	allCandidates = append(allCandidates, nonEvictedSTCandidates...)
+	allCandidates = append(allCandidates, nonEvictedSameQueueCandidates...)
+	return &candidateIterator{
+		runIndex:                          0,
+		frsNeedPreemption:                 frsNeedPreemption,
+		snapshot:                          snapshot,
+		candidates:                        allCandidates,
+		NoCandidateFromOtherQueues:        len(hierarchyCandidates) == 0 && len(priorityCandidates) == 0,
+		NoCandidateForHierarchicalReclaim: len(hierarchyCandidates) == 0,
+		hierarchicalReclaimCtx:            hierarchicalReclaimCtx,
+	}
+}
+
+// Next allows to iterate over the ordered sequence of candidates, with the reason
+// for eviction returned together with a candidate.
+func (c *candidateIterator) Next(borrow bool) (*workload.Info, string) {
+	if c.runIndex >= len(c.candidates) {
+		return nil, ""
+	}
+	candidate := c.candidates[c.runIndex]
+	c.runIndex++
+	if !c.candidateIsValid(candidate, borrow) {
+		return c.Next(borrow)
+	}
+	return candidate.wl, candidate.preemptionVariant.PreemptionReason()
+}
+
+// candidateIsValid checks if candidate is valid,
+// as eg. some candidates can only be considered without borrowing
+// Also, preemption of candidates might invalidate other candidates
+func (c *candidateIterator) candidateIsValid(candidate *candidateElem, borrow bool) bool {
+	if c.hierarchicalReclaimCtx.Cq.Name == candidate.wl.ClusterQueue {
+		return true
+	}
+	if borrow && candidate.preemptionVariant == ReclaimWithoutBorrowing {
+		return false
+	}
+	cq := c.snapshot.ClusterQueue(candidate.wl.ClusterQueue)
+	if cache.IsWithinNominalInResources(cq, c.frsNeedPreemption) {
+		return false
+	}
+	// we don't go all the way to the root but only to the lca node
+	for node := range cq.PathParentToRoot() {
+		if node == candidate.lca {
+			break
+		}
+		if cache.IsWithinNominalInResources(node, c.frsNeedPreemption) {
+			return false
+		}
+	}
+	return true
+}
+
+// Reset moves the candidate iterator back to the starting position.
+// It is required to reset the iterator before each run.
+func (c *candidateIterator) Reset() {
+	c.runIndex = 0
+}
diff --git a/pkg/scheduler/preemption/classical/hierarchical_preemption.go b/pkg/scheduler/preemption/classical/hierarchical_preemption.go
new file mode 100644
index 000000000..80d0aa17f
--- /dev/null
+++ b/pkg/scheduler/preemption/classical/hierarchical_preemption.go
@@ -0,0 +1,234 @@
+/*
+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 classical
+
+import (
+	"k8s.io/apimachinery/pkg/util/sets"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
+	"sigs.k8s.io/kueue/pkg/cache"
+	"sigs.k8s.io/kueue/pkg/resources"
+	"sigs.k8s.io/kueue/pkg/util/priority"
+	"sigs.k8s.io/kueue/pkg/workload"
+)
+
+type preemptionVariant int
+
+const (
+	// Cannot be preempted
+	Never preemptionVariant = iota
+	// Candidate within the same CQ as the preemptor
+	WithinCQ
+	// Preemptor has preferential access to the resources needing preemption
+	// over the candidate, because of its CQ position in the cohort topology.
+	HiearchicalReclaim
+	// Can only be preempted if preemptor CQ (after all preemptions and the
+	// admission of the incoming workload) would not be borrowing any quota
+	ReclaimWithoutBorrowing
+	// Can be preemped even if preemptor CQ would be borrowing
+	ReclaimWhileBorrowing
+)
+
+func (m preemptionVariant) PreemptionReason() string {
+	switch m {
+	case WithinCQ:
+		return kueue.InClusterQueueReason
+	case HiearchicalReclaim:
+		return kueue.InCohortReclamationReason
+	case ReclaimWhileBorrowing:
+		return kueue.InCohortReclaimWhileBorrowingReason
+	case ReclaimWithoutBorrowing:
+		return kueue.InCohortReclamationReason
+	}
+	return "Unknown"
+}
+
+type HierarchicalPreemptionCtx struct {
+	Wl                *kueue.Workload
+	Cq                *cache.ClusterQueueSnapshot
+	FrsNeedPreemption sets.Set[resources.FlavorResource]
+	Requests          resources.FlavorResourceQuantities
+	WorkloadOrdering  workload.Ordering
+}
+
+func IsBorrowingWithinCohortForbidden(cq *cache.ClusterQueueSnapshot) (bool, *int32) {
+	borrowWithinCohort := cq.Preemption.BorrowWithinCohort
+	if borrowWithinCohort == nil || borrowWithinCohort.Policy == kueue.BorrowWithinCohortPolicyNever {
+		return true, nil
+	}
+	return false, borrowWithinCohort.MaxPriorityThreshold
+}
+
+// classifyPreemptionVariant evaluates, based on config and priorities, the
+// preemption type for a given candidate
+func classifyPreemptionVariant(ctx *HierarchicalPreemptionCtx, wl *workload.Info, haveHierarchicalAdvantage bool) preemptionVariant {
+	if !WorkloadUsesResources(wl, ctx.FrsNeedPreemption) {
+		return Never
+	}
+	incomingPriority := priority.Priority(ctx.Wl)
+	candidatePriority := priority.Priority(wl.Obj)
+	if !satisfiesPreemptionPolicy(ctx, wl, incomingPriority, candidatePriority) {
+		return Never
+	}
+	if wl.ClusterQueue == ctx.Cq.Name {
+		return WithinCQ
+	}
+	if haveHierarchicalAdvantage {
+		return HiearchicalReclaim
+	}
+	borrowWithinCohortForbidden, borrowWithinCohortThreshold := IsBorrowingWithinCohortForbidden(ctx.Cq)
+	if borrowWithinCohortForbidden {
+		return ReclaimWithoutBorrowing
+	}
+	if isAboveBorrowingThreshold(candidatePriority, incomingPriority, borrowWithinCohortThreshold) {
+		return ReclaimWithoutBorrowing
+	}
+	return ReclaimWhileBorrowing
+}
+
+func satisfiesPreemptionPolicy(ctx *HierarchicalPreemptionCtx, wl *workload.Info, incomingPriority, candidatePriority int32) bool {
+	var preemptionPolicy kueue.PreemptionPolicy
+	if wl.ClusterQueue == ctx.Cq.Name {
+		preemptionPolicy = ctx.Cq.Preemption.WithinClusterQueue
+	} else {
+		preemptionPolicy = ctx.Cq.Preemption.ReclaimWithinCohort
+	}
+	lowerPriority := incomingPriority > candidatePriority
+	if preemptionPolicy == kueue.PreemptionPolicyLowerPriority {
+		return lowerPriority
+	}
+	if preemptionPolicy == kueue.PreemptionPolicyLowerOrNewerEqualPriority {
+		preemptorTS := ctx.WorkloadOrdering.GetQueueOrderTimestamp(ctx.Wl)
+		newerEqualPriority := (incomingPriority == candidatePriority) && preemptorTS.Before(ctx.WorkloadOrdering.GetQueueOrderTimestamp(wl.Obj))
+		return (lowerPriority || newerEqualPriority)
+	}
+	return preemptionPolicy == kueue.PreemptionPolicyAny
+}
+
+func isAboveBorrowingThreshold(candidatePriority, incomingPriority int32, borrowWithinCohortThreshold *int32) bool {
+	if candidatePriority >= incomingPriority {
+		return true
+	}
+	if borrowWithinCohortThreshold == nil {
+		return false
+	}
+	return candidatePriority > *borrowWithinCohortThreshold
+}
+
+func collectSameQueueCandidates(ctx *HierarchicalPreemptionCtx) []*candidateElem {
+	if ctx.Cq.Preemption.WithinClusterQueue == kueue.PreemptionPolicyNever {
+		return []*candidateElem{}
+	}
+	return getCandidatesFromCQ(ctx.Cq, nil, ctx, false)
+}
+
+func getCandidatesFromCQ(cq *cache.ClusterQueueSnapshot, lca *cache.CohortSnapshot, ctx *HierarchicalPreemptionCtx, hasHiearchicalAdvantage bool) []*candidateElem {
+	candidates := []*candidateElem{}
+	for _, candidateWl := range cq.Workloads {
+		preemptionVariant := classifyPreemptionVariant(ctx, candidateWl, hasHiearchicalAdvantage)
+		if preemptionVariant == Never {
+			continue
+		}
+		candidates = append(candidates,
+			&candidateElem{
+				wl:                candidateWl,
+				lca:               lca,
+				preemptionVariant: preemptionVariant,
+			})
+	}
+	return candidates
+}
+
+func collectCandidatesForHierarchicalReclaim(ctx *HierarchicalPreemptionCtx) ([]*candidateElem, []*candidateElem) {
+	hierarchyCandidates := []*candidateElem{}
+	priorityCandidates := []*candidateElem{}
+	if !ctx.Cq.HasParent() || ctx.Cq.Preemption.ReclaimWithinCohort == kueue.PreemptionPolicyNever {
+		return hierarchyCandidates, priorityCandidates
+	}
+	var previousSubtreeRoot *cache.CohortSnapshot
+	var candidateList *[]*candidateElem
+	var fits bool
+	hasHierarchicalAdvantage, remainingRequests := cache.QuantitiesFitInQuota(ctx.Cq, ctx.Requests)
+	for currentSubtreeRoot := range ctx.Cq.PathParentToRoot() {
+		if hasHierarchicalAdvantage {
+			candidateList = &hierarchyCandidates
+		} else {
+			candidateList = &priorityCandidates
+		}
+		collectCandidatesInSubtree(ctx, currentSubtreeRoot, currentSubtreeRoot, previousSubtreeRoot, hasHierarchicalAdvantage, candidateList)
+		fits, remainingRequests = cache.QuantitiesFitInQuota(currentSubtreeRoot, remainingRequests)
+		// Once we find a subtree sT that fits the requests, we will look for workloads that use quota
+		// of that subtree. The preemptor will have hierarchical advantage over all such workloads
+		// because it belongs to subtree sT. For that reason variable hasHierarchicalAdvantage
+		// remains true in subsequent iterations of the loop.
+		hasHierarchicalAdvantage = hasHierarchicalAdvantage || fits
+		previousSubtreeRoot = currentSubtreeRoot
+	}
+	return hierarchyCandidates, priorityCandidates
+}
+
+// visit the nodes in the hierarchy and collect the ones that exceed quota
+// avoid subtrees that are within quota and the skipped subtree
+func collectCandidatesInSubtree(ctx *HierarchicalPreemptionCtx, currentCohort *cache.CohortSnapshot, subtreeRoot *cache.CohortSnapshot, skipSubtree *cache.CohortSnapshot, hasHierarchicalAdvantage bool, result *[]*candidateElem) {
+	for _, childCohort := range currentCohort.ChildCohorts() {
+		// we already processed this subtree
+		if childCohort == skipSubtree {
+			continue
+		}
+		// don't look for candidates in subtrees that are not exceeding their quotas
+		if cache.IsWithinNominalInResources(childCohort, ctx.FrsNeedPreemption) {
+			continue
+		}
+		collectCandidatesInSubtree(ctx, childCohort, subtreeRoot, skipSubtree, hasHierarchicalAdvantage, result)
+	}
+	for _, childCq := range currentCohort.ChildCQs() {
+		if childCq == ctx.Cq {
+			continue
+		}
+		if !cache.IsWithinNominalInResources(childCq, ctx.FrsNeedPreemption) {
+			*result = append(*result, getCandidatesFromCQ(childCq, subtreeRoot, ctx, hasHierarchicalAdvantage)...)
+		}
+	}
+}
+
+// getNodeHeight calculates the distance to the furthest leaf
+func getNodeHeight(node *cache.CohortSnapshot) int {
+	maxHeight := min(node.ChildCount(), 1)
+	for _, childCohort := range node.ChildCohorts() {
+		maxHeight = max(maxHeight, getNodeHeight(childCohort)+1)
+	}
+	return maxHeight
+}
+
+// FindHeightOfLowestSubtreeThatFits returns height of a lowest subtree in the cohort
+// that fits additional val of resource fr. If no such subtree exists, it returns
+// height the whole cohort hierarchy. Note that height of a trivial subtree
+// with only one node is 0. It also returns if the returned subtree is smaller than the whole cohort tree.
+func FindHeightOfLowestSubtreeThatFits(c *cache.ClusterQueueSnapshot, fr resources.FlavorResource, val int64) (int, bool) {
+	if !(c.BorrowingWith(fr, val) && c.HasParent()) {
+		return 0, c.HasParent()
+	}
+	remaining := val - cache.LocalAvailable(c, fr)
+	for trackingNode := range c.PathParentToRoot() {
+		if !trackingNode.BorrowingWith(fr, remaining) {
+			return getNodeHeight(trackingNode), trackingNode.HasParent()
+		}
+		remaining -= cache.LocalAvailable(trackingNode, fr)
+	}
+	// no fit found
+	return getNodeHeight(c.Parent().Root()), false
+}
diff --git a/pkg/scheduler/preemption/preemption.go b/pkg/scheduler/preemption/preemption.go
index a3f5f35c5..a089a0e65 100644
--- a/pkg/scheduler/preemption/preemption.go
+++ b/pkg/scheduler/preemption/preemption.go
@@ -42,6 +42,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/metrics"
 	"sigs.k8s.io/kueue/pkg/resources"
 	"sigs.k8s.io/kueue/pkg/scheduler/flavorassigner"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/classical"
 	"sigs.k8s.io/kueue/pkg/scheduler/preemption/fairsharing"
 	"sigs.k8s.io/kueue/pkg/util/priority"
 	"sigs.k8s.io/kueue/pkg/util/routine"
@@ -97,26 +98,6 @@ func (p *Preemptor) OverrideApply(f func(context.Context, *kueue.Workload, strin
 	p.applyPreemption = f
 }
 
-func candidatesOnlyFromQueue(candidates []*workload.Info, clusterQueue kueue.ClusterQueueReference) []*workload.Info {
-	result := make([]*workload.Info, 0, len(candidates))
-	for _, wi := range candidates {
-		if wi.ClusterQueue == clusterQueue {
-			result = append(result, wi)
-		}
-	}
-	return result
-}
-
-func candidatesFromCQOrUnderThreshold(candidates []*workload.Info, clusterQueue kueue.ClusterQueueReference, threshold int32) []*workload.Info {
-	result := make([]*workload.Info, 0, len(candidates))
-	for _, wi := range candidates {
-		if wi.ClusterQueue == clusterQueue || priority.Priority(wi.Obj) < threshold {
-			result = append(result, wi)
-		}
-	}
-	return result
-}
-
 type Target struct {
 	WorkloadInfo *workload.Info
 	Reason       string
@@ -142,65 +123,10 @@ func (p *Preemptor) GetTargets(log logr.Logger, wl workload.Info, assignment fla
 }
 
 func (p *Preemptor) getTargets(preemptionCtx *preemptionCtx) []*Target {
-	candidates := p.findCandidates(preemptionCtx.preemptor.Obj, preemptionCtx.preemptorCQ, preemptionCtx.frsNeedPreemption)
-	if len(candidates) == 0 {
-		return nil
-	}
-	sort.Slice(candidates, candidatesOrdering(candidates, preemptionCtx.preemptorCQ.Name, p.clock.Now()))
 	if p.enableFairSharing {
-		return fairPreemptions(preemptionCtx, candidates, p.fsStrategies)
-	}
-
-	sameQueueCandidates := candidatesOnlyFromQueue(candidates, preemptionCtx.preemptorCQ.Name)
-
-	// To avoid flapping, Kueue only allows preemption of workloads from the same
-	// queue if borrowing. Preemption of workloads from queues can happen only
-	// if not borrowing at the same time. Kueue prioritizes preemption of
-	// workloads from the other queues (that borrowed resources) first, before
-	// trying to preempt more own workloads and borrow at the same time.
-	if len(sameQueueCandidates) == len(candidates) {
-		// There is no possible preemption of workloads from other queues,
-		// so we'll try borrowing.
-		return minimalPreemptions(preemptionCtx, candidates, true, nil)
-	}
-
-	// There is a potential of preemption of workloads from the other queue in the
-	// cohort. We proceed with borrowing only if the dedicated policy
-	// (borrowWithinCohort) is enabled. This ensures the preempted workloads
-	// have lower priority, and so they will not preempt the preemptor when
-	// requeued.
-	if borrowWithinCohort, thresholdPrio := canBorrowWithinCohort(preemptionCtx); borrowWithinCohort {
-		if !queueUnderNominalInResourcesNeedingPreemption(preemptionCtx) {
-			// It can only preempt workloads from another CQ if they are strictly under allowBorrowingBelowPriority.
-			candidates = candidatesFromCQOrUnderThreshold(candidates, preemptionCtx.preemptor.ClusterQueue, *thresholdPrio)
-		}
-		return minimalPreemptions(preemptionCtx, candidates, true, thresholdPrio)
-	}
-
-	// Only try preemptions in the cohort, without borrowing, if the target clusterqueue is still
-	// under nominal quota for all resources.
-	if queueUnderNominalInResourcesNeedingPreemption(preemptionCtx) {
-		if targets := minimalPreemptions(preemptionCtx, candidates, false, nil); len(targets) > 0 {
-			return targets
-		}
+		return p.fairPreemptions(preemptionCtx, p.fsStrategies)
 	}
-
-	// Final attempt. This time only candidates from the same queue, but
-	// with borrowing.
-	return minimalPreemptions(preemptionCtx, sameQueueCandidates, true, nil)
-}
-
-// canBorrowWithinCohort returns whether the behavior is enabled for the ClusterQueue and the threshold priority to use.
-func canBorrowWithinCohort(preemptionCtx *preemptionCtx) (bool, *int32) {
-	borrowWithinCohort := preemptionCtx.preemptorCQ.Preemption.BorrowWithinCohort
-	if borrowWithinCohort == nil || borrowWithinCohort.Policy == kueue.BorrowWithinCohortPolicyNever {
-		return false, nil
-	}
-	threshold := priority.Priority(preemptionCtx.preemptor.Obj)
-	if borrowWithinCohort.MaxPriorityThreshold != nil && *borrowWithinCohort.MaxPriorityThreshold < threshold {
-		threshold = *borrowWithinCohort.MaxPriorityThreshold + 1
-	}
-	return true, &threshold
+	return p.classicalPreemptions(preemptionCtx)
 }
 
 var HumanReadablePreemptionReasons = map[string]string{
@@ -264,66 +190,71 @@ func (p *Preemptor) applyPreemptionWithSSA(ctx context.Context, w *kueue.Workloa
 	return workload.ApplyAdmissionStatus(ctx, p.client, w, true, p.clock)
 }
 
-// minimalPreemptions implements a heuristic to find a minimal set of Workloads
+type preemptionAttemptOpts struct {
+	borrowing bool
+}
+
+// classicalPreemptions implements a heuristic to find a minimal set of Workloads
 // to preempt.
 // The heuristic first removes candidates, in the input order, while their
 // ClusterQueues are still borrowing resources and while the incoming Workload
 // doesn't fit in the quota.
 // Once the Workload fits, the heuristic tries to add Workloads back, in the
 // reverse order in which they were removed, while the incoming Workload still
-// fits.
-func minimalPreemptions(preemptionCtx *preemptionCtx, candidates []*workload.Info, allowBorrowing bool, allowBorrowingBelowPriority *int32) []*Target {
-	if logV := preemptionCtx.log.V(5); logV.Enabled() {
-		logV.Info("Simulating preemption", "candidates", workload.References(candidates), "resourcesRequiringPreemption", preemptionCtx.frsNeedPreemption.UnsortedList(), "allowBorrowing", allowBorrowing, "allowBorrowingBelowPriority", allowBorrowingBelowPriority, "preemptingWorkload", klog.KObj(preemptionCtx.preemptor.Obj))
-	}
-	// Simulate removing all candidates from the ClusterQueue and cohort.
-	var targets []*Target
-	fits := false
-	for _, candWl := range candidates {
-		candCQ := preemptionCtx.snapshot.ClusterQueue(candWl.ClusterQueue)
-		reason := kueue.InClusterQueueReason
-		if preemptionCtx.preemptorCQ != candCQ {
-			if !cqIsBorrowing(candCQ, preemptionCtx.frsNeedPreemption) {
-				continue
-			}
-			reason = kueue.InCohortReclamationReason
-			if allowBorrowingBelowPriority != nil {
-				if priority.Priority(candWl.Obj) >= *allowBorrowingBelowPriority {
-					// We set allowBorrowing=false if there is a candidate with priority
-					// exceeding allowBorrowingBelowPriority added to targets.
-					//
-					// We need to be careful mutating allowBorrowing. We rely on the
-					// fact that once there is a candidate exceeding the priority added
-					// to targets, then at least one such candidate is present in the
-					// final set of targets (after the second phase of the function).
-					//
-					// This is true, because the candidates are ordered according
-					// to priorities (from lowest to highest, using candidatesOrdering),
-					// and the last added target is not removed in the second phase of
-					// the function.
-					allowBorrowing = false
-				} else {
-					reason = kueue.InCohortReclaimWhileBorrowingReason
-				}
+// fits
+func (p *Preemptor) classicalPreemptions(preemptionCtx *preemptionCtx) []*Target {
+	hierarchicalReclaimCtx := &classical.HierarchicalPreemptionCtx{
+		Wl:                preemptionCtx.preemptor.Obj,
+		Cq:                preemptionCtx.preemptorCQ,
+		FrsNeedPreemption: preemptionCtx.frsNeedPreemption,
+		Requests:          preemptionCtx.workloadUsage.Quota,
+		WorkloadOrdering:  p.workloadOrdering,
+	}
+	candidatesGenerator := classical.NewCandidateIterator(hierarchicalReclaimCtx, preemptionCtx.frsNeedPreemption, preemptionCtx.snapshot, p.clock, CandidatesOrdering)
+	var attemptPossibleOpts []preemptionAttemptOpts
+	borrowWithinCohortForbidden, _ := classical.IsBorrowingWithinCohortForbidden(preemptionCtx.preemptorCQ)
+	// We have three types of candidates:
+	// 1. Hierarchy candidates. Candidates over which the incoming workload has a
+	// 	  hierarchical advantage (it is closer to the quota used by the candidate).
+	//    We can preempt such candidates regardless of their priority.
+	// 2. Priority candidates. Candidates over which there is no hiearchical advantage
+	//    but the possibility to preempt is determined based on priorities.
+	// 	  We respect the BorrowWithinCohort configuration only for these candidates.
+	// 3. Same queue candidates.
+	// We can only preempt a priority candidate with priority > MaxPriorityThreshold
+	// if the target CQ is not borrowing (by the definition of the MaxPriorityThreshold).
+	// We sometimes need to consider both options allowBorrowing = true and false
+	// (because with false we have more candidates but cannot use borrowing).
+	// The order in which the options are considered is arbitrary and the condition
+	// in which we try allowBorrowing=false before true is to keep compatibility with
+	// previous versions.
+	switch {
+	case candidatesGenerator.NoCandidateFromOtherQueues || (borrowWithinCohortForbidden && !queueUnderNominalInResourcesNeedingPreemption(preemptionCtx)):
+		attemptPossibleOpts = []preemptionAttemptOpts{{true}}
+	case borrowWithinCohortForbidden && candidatesGenerator.NoCandidateForHierarchicalReclaim:
+		attemptPossibleOpts = []preemptionAttemptOpts{{false}, {true}}
+	default:
+		attemptPossibleOpts = []preemptionAttemptOpts{{true}, {false}}
+	}
+
+	for _, attemptOpts := range attemptPossibleOpts {
+		var targets []*Target
+		candidatesGenerator.Reset()
+		for candidate, reason := candidatesGenerator.Next(attemptOpts.borrowing); candidate != nil; candidate, reason = candidatesGenerator.Next(attemptOpts.borrowing) {
+			preemptionCtx.snapshot.RemoveWorkload(candidate)
+			targets = append(targets, &Target{
+				WorkloadInfo: candidate,
+				Reason:       reason,
+			})
+			if workloadFits(preemptionCtx, attemptOpts.borrowing) {
+				targets = fillBackWorkloads(preemptionCtx, targets, attemptOpts.borrowing)
+				restoreSnapshot(preemptionCtx.snapshot, targets)
+				return targets
 			}
 		}
-		preemptionCtx.snapshot.RemoveWorkload(candWl)
-		targets = append(targets, &Target{
-			WorkloadInfo: candWl,
-			Reason:       reason,
-		})
-		if workloadFits(preemptionCtx, allowBorrowing) {
-			fits = true
-			break
-		}
-	}
-	if !fits {
 		restoreSnapshot(preemptionCtx.snapshot, targets)
-		return nil
 	}
-	targets = fillBackWorkloads(preemptionCtx, targets, allowBorrowing)
-	restoreSnapshot(preemptionCtx.snapshot, targets)
-	return targets
+	return nil
 }
 
 func fillBackWorkloads(preemptionCtx *preemptionCtx, targets []*Target, allowBorrowing bool) []*Target {
@@ -439,7 +370,12 @@ func runSecondFsStrategy(retryCandidates []*workload.Info, preemptionCtx *preemp
 	return false, targets
 }
 
-func fairPreemptions(preemptionCtx *preemptionCtx, candidates []*workload.Info, strategies []fairsharing.Strategy) []*Target {
+func (p *Preemptor) fairPreemptions(preemptionCtx *preemptionCtx, strategies []fairsharing.Strategy) []*Target {
+	candidates := p.findCandidates(preemptionCtx.preemptor.Obj, preemptionCtx.preemptorCQ, preemptionCtx.frsNeedPreemption)
+	if len(candidates) == 0 {
+		return nil
+	}
+	sort.Slice(candidates, CandidatesOrdering(candidates, preemptionCtx.preemptorCQ.Name, p.clock.Now()))
 	if logV := preemptionCtx.log.V(5); logV.Enabled() {
 		logV.Info("Simulating fair preemption", "candidates", workload.References(candidates), "resourcesRequiringPreemption", preemptionCtx.frsNeedPreemption.UnsortedList(), "preemptingWorkload", klog.KObj(preemptionCtx.preemptor.Obj))
 	}
@@ -495,7 +431,7 @@ func (p *Preemptor) findCandidates(wl *kueue.Workload, cq *cache.ClusterQueueSna
 				continue
 			}
 
-			if !workloadUsesResources(candidateWl, frsNeedPreemption) {
+			if !classical.WorkloadUsesResources(candidateWl, frsNeedPreemption) {
 				continue
 			}
 			candidates = append(candidates, candidateWl)
@@ -513,7 +449,7 @@ func (p *Preemptor) findCandidates(wl *kueue.Workload, cq *cache.ClusterQueueSna
 				if onlyLowerPriority && priority.Priority(candidateWl.Obj) >= priority.Priority(wl) {
 					continue
 				}
-				if !workloadUsesResources(candidateWl, frsNeedPreemption) {
+				if !classical.WorkloadUsesResources(candidateWl, frsNeedPreemption) {
 					continue
 				}
 				candidates = append(candidates, candidateWl)
@@ -535,17 +471,6 @@ func cqIsBorrowing(cq *cache.ClusterQueueSnapshot, frsNeedPreemption sets.Set[re
 	return false
 }
 
-func workloadUsesResources(wl *workload.Info, frsNeedPreemption sets.Set[resources.FlavorResource]) bool {
-	for _, ps := range wl.TotalRequests {
-		for res, flv := range ps.Flavors {
-			if frsNeedPreemption.Has(resources.FlavorResource{Flavor: flv, Resource: res}) {
-				return true
-			}
-		}
-	}
-	return false
-}
-
 // workloadFits determines if the workload requests would fit given the
 // requestable resources and simulated usage of the ClusterQueue and its cohort,
 // if it belongs to one.
@@ -588,7 +513,7 @@ func queueUnderNominalInResourcesNeedingPreemption(preemptionCtx *preemptionCtx)
 // same ClusterQueue as the preemptor.
 // 2. Workloads with lower priority first.
 // 3. Workloads admitted more recently first.
-func candidatesOrdering(candidates []*workload.Info, cq kueue.ClusterQueueReference, now time.Time) func(int, int) bool {
+func CandidatesOrdering(candidates []*workload.Info, cq kueue.ClusterQueueReference, now time.Time) func(int, int) bool {
 	return func(i, j int) bool {
 		a := candidates[i]
 		b := candidates[j]
diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go
index 8e24abe4e..05fda284a 100644
--- a/pkg/scheduler/scheduler.go
+++ b/pkg/scheduler/scheduler.go
@@ -402,7 +402,7 @@ func quotaResourcesToReserve(e *entry, cq *cache.ClusterQueueSnapshot) resources
 	reservedUsage := make(resources.FlavorResourceQuantities)
 	for fr, usage := range e.assignment.Usage.Quota {
 		cqQuota := cq.QuotaFor(fr)
-		if e.assignment.Borrowing {
+		if e.assignment.Borrowing > 0 {
 			if cqQuota.BorrowingLimit == nil {
 				reservedUsage[fr] = usage
 			} else {
@@ -580,7 +580,7 @@ func (e entryOrdering) Less(i, j int) bool {
 	aBorrows := a.assignment.Borrows()
 	bBorrows := b.assignment.Borrows()
 	if aBorrows != bBorrows {
-		return !aBorrows
+		return aBorrows < bBorrows
 	}
 
 	// 2. Higher priority first if not disabled.
@@ -651,8 +651,7 @@ func (s *Scheduler) requeueAndUpdate(ctx context.Context, e entry) {
 	log.V(2).Info("Workload re-queued", "workload", klog.KObj(e.Obj), "clusterQueue", klog.KRef("", string(e.ClusterQueue)), "queue", klog.KRef(e.Obj.Namespace, e.Obj.Spec.QueueName), "requeueReason", e.requeueReason, "added", added, "status", e.status)
 
 	if e.status == notNominated || e.status == skipped {
-		patch := workload.BaseSSAWorkload(e.Obj)
-		workload.AdmissionStatusPatch(e.Obj, patch, true)
+		patch := workload.PrepareWorkloadPatch(e.Obj, true, s.clock)
 		reservationIsChanged := workload.UnsetQuotaReservationWithCondition(patch, "Pending", e.inadmissibleMsg, s.clock.Now())
 		resourceRequestsIsChanged := workload.PropagateResourceRequests(patch, &e.Info)
 		if reservationIsChanged || resourceRequestsIsChanged {
diff --git a/pkg/workload/workload.go b/pkg/workload/workload.go
index 53c62bb4e..f66e871d6 100644
--- a/pkg/workload/workload.go
+++ b/pkg/workload/workload.go
@@ -709,10 +709,15 @@ func AdmissionChecksStatusPatch(w *kueue.Workload, wlCopy *kueue.Workload, c clo
 // If strict is true, resourceVersion will be part of the patch, make this call fail if Workload
 // was changed.
 func ApplyAdmissionStatus(ctx context.Context, c client.Client, w *kueue.Workload, strict bool, clk clock.Clock) error {
+	wlCopy := PrepareWorkloadPatch(w, strict, clk)
+	return ApplyAdmissionStatusPatch(ctx, c, wlCopy)
+}
+
+func PrepareWorkloadPatch(w *kueue.Workload, strict bool, clk clock.Clock) *kueue.Workload {
 	wlCopy := BaseSSAWorkload(w)
 	AdmissionStatusPatch(w, wlCopy, strict)
 	AdmissionChecksStatusPatch(w, wlCopy, clk)
-	return ApplyAdmissionStatusPatch(ctx, c, wlCopy)
+	return wlCopy
 }
 
 // ApplyAdmissionStatusPatch applies the patch of admission related status fields of a workload with SSA.
@@ -788,8 +793,7 @@ func IsActive(w *kueue.Workload) bool {
 // IsEvictedByDeactivation returns true if the workload is evicted by deactivation.
 func IsEvictedByDeactivation(w *kueue.Workload) bool {
 	cond := apimeta.FindStatusCondition(w.Status.Conditions, kueue.WorkloadEvicted)
-	return cond != nil && cond.Status == metav1.ConditionTrue &&
-		(strings.HasPrefix(cond.Reason, kueue.WorkloadDeactivated) || strings.HasPrefix(cond.Reason, kueue.WorkloadEvictedByDeactivation))
+	return cond != nil && cond.Status == metav1.ConditionTrue && strings.HasPrefix(cond.Reason, kueue.WorkloadDeactivated)
 }
 
 func IsEvictedByPodsReadyTimeout(w *kueue.Workload) (*metav1.Condition, bool) {
diff --git a/site/content/en/docs/installation/_index.md b/site/content/en/docs/installation/_index.md
index 049cace45..07657d2d5 100644
--- a/site/content/en/docs/installation/_index.md
+++ b/site/content/en/docs/installation/_index.md
@@ -294,7 +294,6 @@ spec:
 |---------------------------------------|---------|------------|-------|-------|
 | `QueueVisibility`                     | `false` | Alpha      | 0.4   | 0.9   |
 | `QueueVisibility`                     | `false` | Deprecated | 0.9   |       |
-| `KeepQuotaForProvReqRetry`            | `false` | Deprecated | 0.9   |       |
 | `MultiplePreemptions`                 | `false` | Alpha      | 0.8   | 0.8   |
 | `MultiplePreemptions`                 | `true`  | Beta       | 0.9   | 0.9   |
 | `MultiplePreemptions`                 | `true`  | GA         | 0.10  |       |
diff --git a/site/content/en/docs/talks_and_presentations/_index.md b/site/content/en/docs/talks_and_presentations/_index.md
index ea9ee25a3..dc443fa76 100644
--- a/site/content/en/docs/talks_and_presentations/_index.md
+++ b/site/content/en/docs/talks_and_presentations/_index.md
@@ -9,6 +9,23 @@ description: >
   Talks and presentations showcasing Kueue
 ---
 
+# 2025
+
+* KubeCon EU, April, London<br>
+  Accelerate Your AI/ML Workloads With Topology-Aware Scheduling in Kueue<br>
+  Michał Woźniak, Google & Yuki Iwai, CyberAgent, Inc.<br>
+  [talk details](https://sched.co/1u5fo) | [video](https://youtu.be/F55pFM1M1bU)<br>
+
+* KubeCon EU, April, London<br>
+  Tutorial: Build, Operate, and Use a Multi-Tenant AI Cluster Based Entirely on Open Source<br>
+  Claudia Misale, Olivier Tardieu, & David Grove, IBM Research<br>
+  [talk details](https://sched.co/1tx6h) | [video](https://youtu.be/Ab7mRoJYsMo) | [tutorial materials](https://github.com/project-codeflare/mlbatch/tree/main/setup.KubeConEU25)<br>
+
+* KubeCon EU, April, London<br>
+  Kueue: Save Some QPS for the Rest of Us! How To Manage 100k Updates Per Second<br>
+  Patryk Bundyra, Google<br>
+  [talk details](https://sched.co/1txCn) | [video](https://youtu.be/njNXlZNT3dw)<br>
+
 # 2024
 
 * KubeCon NA, November, Salt Lake City<br>
diff --git a/site/content/en/docs/tasks/manage/monitor_pending_workloads/pending_workloads_on_demand.md b/site/content/en/docs/tasks/manage/monitor_pending_workloads/pending_workloads_on_demand.md
index 434b05f9f..e635e60f5 100644
--- a/site/content/en/docs/tasks/manage/monitor_pending_workloads/pending_workloads_on_demand.md
+++ b/site/content/en/docs/tasks/manage/monitor_pending_workloads/pending_workloads_on_demand.md
@@ -22,14 +22,6 @@ Make sure the following conditions are met:
 - The kubectl command-line tool has communication with your cluster.
 - [Kueue is installed](/docs/installation) in version v0.6.0 or later.
 
-### Configure API Priority and Fairness:
-
-To install the [API Priority and Fairness](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/) configuration for the visibility API apply manifests:
-
-```shell
-kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/{{< param "version" >}}/visibility-apf.yaml
-```
-
 ### Directly accessing the Visibility API
 
 If you want to directly access the Visibility API with a http client like 
diff --git a/vendor/golang.org/x/net/html/atom/table.go b/vendor/golang.org/x/net/html/atom/table.go
index 2a938864c..b460e6f72 100644
--- a/vendor/golang.org/x/net/html/atom/table.go
+++ b/vendor/golang.org/x/net/html/atom/table.go
@@ -11,23 +11,23 @@ const (
 	AcceptCharset             Atom = 0x1a0e
 	Accesskey                 Atom = 0x2c09
 	Acronym                   Atom = 0xaa07
-	Action                    Atom = 0x27206
-	Address                   Atom = 0x6f307
+	Action                    Atom = 0x26506
+	Address                   Atom = 0x6f107
 	Align                     Atom = 0xb105
-	Allowfullscreen           Atom = 0x2080f
+	Allowfullscreen           Atom = 0x3280f
 	Allowpaymentrequest       Atom = 0xc113
 	Allowusermedia            Atom = 0xdd0e
 	Alt                       Atom = 0xf303
 	Annotation                Atom = 0x1c90a
 	AnnotationXml             Atom = 0x1c90e
-	Applet                    Atom = 0x31906
-	Area                      Atom = 0x35604
-	Article                   Atom = 0x3fc07
+	Applet                    Atom = 0x30806
+	Area                      Atom = 0x35004
+	Article                   Atom = 0x3f607
 	As                        Atom = 0x3c02
 	Aside                     Atom = 0x10705
 	Async                     Atom = 0xff05
 	Audio                     Atom = 0x11505
-	Autocomplete              Atom = 0x2780c
+	Autocomplete              Atom = 0x26b0c
 	Autofocus                 Atom = 0x12109
 	Autoplay                  Atom = 0x13c08
 	B                         Atom = 0x101
@@ -43,34 +43,34 @@ const (
 	Br                        Atom = 0x202
 	Button                    Atom = 0x19106
 	Canvas                    Atom = 0x10306
-	Caption                   Atom = 0x23107
-	Center                    Atom = 0x22006
-	Challenge                 Atom = 0x29b09
+	Caption                   Atom = 0x22407
+	Center                    Atom = 0x21306
+	Challenge                 Atom = 0x28e09
 	Charset                   Atom = 0x2107
-	Checked                   Atom = 0x47907
+	Checked                   Atom = 0x5b507
 	Cite                      Atom = 0x19c04
-	Class                     Atom = 0x56405
-	Code                      Atom = 0x5c504
+	Class                     Atom = 0x55805
+	Code                      Atom = 0x5ee04
 	Col                       Atom = 0x1ab03
 	Colgroup                  Atom = 0x1ab08
 	Color                     Atom = 0x1bf05
 	Cols                      Atom = 0x1c404
 	Colspan                   Atom = 0x1c407
 	Command                   Atom = 0x1d707
-	Content                   Atom = 0x58b07
-	Contenteditable           Atom = 0x58b0f
-	Contextmenu               Atom = 0x3800b
+	Content                   Atom = 0x57b07
+	Contenteditable           Atom = 0x57b0f
+	Contextmenu               Atom = 0x37a0b
 	Controls                  Atom = 0x1de08
-	Coords                    Atom = 0x1ea06
-	Crossorigin               Atom = 0x1fb0b
-	Data                      Atom = 0x4a504
-	Datalist                  Atom = 0x4a508
-	Datetime                  Atom = 0x2b808
-	Dd                        Atom = 0x2d702
+	Coords                    Atom = 0x1f006
+	Crossorigin               Atom = 0x1fa0b
+	Data                      Atom = 0x49904
+	Datalist                  Atom = 0x49908
+	Datetime                  Atom = 0x2ab08
+	Dd                        Atom = 0x2bf02
 	Default                   Atom = 0x10a07
-	Defer                     Atom = 0x5c705
-	Del                       Atom = 0x45203
-	Desc                      Atom = 0x56104
+	Defer                     Atom = 0x5f005
+	Del                       Atom = 0x44c03
+	Desc                      Atom = 0x55504
 	Details                   Atom = 0x7207
 	Dfn                       Atom = 0x8703
 	Dialog                    Atom = 0xbb06
@@ -78,106 +78,106 @@ const (
 	Dirname                   Atom = 0x9307
 	Disabled                  Atom = 0x16408
 	Div                       Atom = 0x16b03
-	Dl                        Atom = 0x5e602
-	Download                  Atom = 0x46308
+	Dl                        Atom = 0x5d602
+	Download                  Atom = 0x45d08
 	Draggable                 Atom = 0x17a09
-	Dropzone                  Atom = 0x40508
-	Dt                        Atom = 0x64b02
+	Dropzone                  Atom = 0x3ff08
+	Dt                        Atom = 0x64002
 	Em                        Atom = 0x6e02
 	Embed                     Atom = 0x6e05
-	Enctype                   Atom = 0x28d07
-	Face                      Atom = 0x21e04
-	Fieldset                  Atom = 0x22608
-	Figcaption                Atom = 0x22e0a
-	Figure                    Atom = 0x24806
+	Enctype                   Atom = 0x28007
+	Face                      Atom = 0x21104
+	Fieldset                  Atom = 0x21908
+	Figcaption                Atom = 0x2210a
+	Figure                    Atom = 0x23b06
 	Font                      Atom = 0x3f04
 	Footer                    Atom = 0xf606
-	For                       Atom = 0x25403
-	ForeignObject             Atom = 0x2540d
-	Foreignobject             Atom = 0x2610d
-	Form                      Atom = 0x26e04
-	Formaction                Atom = 0x26e0a
-	Formenctype               Atom = 0x2890b
-	Formmethod                Atom = 0x2a40a
-	Formnovalidate            Atom = 0x2ae0e
-	Formtarget                Atom = 0x2c00a
+	For                       Atom = 0x24703
+	ForeignObject             Atom = 0x2470d
+	Foreignobject             Atom = 0x2540d
+	Form                      Atom = 0x26104
+	Formaction                Atom = 0x2610a
+	Formenctype               Atom = 0x27c0b
+	Formmethod                Atom = 0x2970a
+	Formnovalidate            Atom = 0x2a10e
+	Formtarget                Atom = 0x2b30a
 	Frame                     Atom = 0x8b05
 	Frameset                  Atom = 0x8b08
 	H1                        Atom = 0x15c02
-	H2                        Atom = 0x2de02
-	H3                        Atom = 0x30d02
-	H4                        Atom = 0x34502
-	H5                        Atom = 0x34f02
-	H6                        Atom = 0x64d02
-	Head                      Atom = 0x33104
-	Header                    Atom = 0x33106
-	Headers                   Atom = 0x33107
+	H2                        Atom = 0x56102
+	H3                        Atom = 0x2cd02
+	H4                        Atom = 0x2fc02
+	H5                        Atom = 0x33f02
+	H6                        Atom = 0x34902
+	Head                      Atom = 0x32004
+	Header                    Atom = 0x32006
+	Headers                   Atom = 0x32007
 	Height                    Atom = 0x5206
-	Hgroup                    Atom = 0x2ca06
-	Hidden                    Atom = 0x2d506
-	High                      Atom = 0x2db04
+	Hgroup                    Atom = 0x64206
+	Hidden                    Atom = 0x2bd06
+	High                      Atom = 0x2ca04
 	Hr                        Atom = 0x15702
-	Href                      Atom = 0x2e004
-	Hreflang                  Atom = 0x2e008
+	Href                      Atom = 0x2cf04
+	Hreflang                  Atom = 0x2cf08
 	Html                      Atom = 0x5604
-	HttpEquiv                 Atom = 0x2e80a
+	HttpEquiv                 Atom = 0x2d70a
 	I                         Atom = 0x601
-	Icon                      Atom = 0x58a04
+	Icon                      Atom = 0x57a04
 	Id                        Atom = 0x10902
-	Iframe                    Atom = 0x2fc06
-	Image                     Atom = 0x30205
-	Img                       Atom = 0x30703
-	Input                     Atom = 0x44b05
-	Inputmode                 Atom = 0x44b09
-	Ins                       Atom = 0x20403
-	Integrity                 Atom = 0x23f09
+	Iframe                    Atom = 0x2eb06
+	Image                     Atom = 0x2f105
+	Img                       Atom = 0x2f603
+	Input                     Atom = 0x44505
+	Inputmode                 Atom = 0x44509
+	Ins                       Atom = 0x20303
+	Integrity                 Atom = 0x23209
 	Is                        Atom = 0x16502
-	Isindex                   Atom = 0x30f07
-	Ismap                     Atom = 0x31605
-	Itemid                    Atom = 0x38b06
+	Isindex                   Atom = 0x2fe07
+	Ismap                     Atom = 0x30505
+	Itemid                    Atom = 0x38506
 	Itemprop                  Atom = 0x19d08
-	Itemref                   Atom = 0x3cd07
-	Itemscope                 Atom = 0x67109
-	Itemtype                  Atom = 0x31f08
+	Itemref                   Atom = 0x3c707
+	Itemscope                 Atom = 0x66f09
+	Itemtype                  Atom = 0x30e08
 	Kbd                       Atom = 0xb903
 	Keygen                    Atom = 0x3206
 	Keytype                   Atom = 0xd607
 	Kind                      Atom = 0x17704
 	Label                     Atom = 0x5905
-	Lang                      Atom = 0x2e404
+	Lang                      Atom = 0x2d304
 	Legend                    Atom = 0x18106
 	Li                        Atom = 0xb202
 	Link                      Atom = 0x17404
-	List                      Atom = 0x4a904
-	Listing                   Atom = 0x4a907
+	List                      Atom = 0x49d04
+	Listing                   Atom = 0x49d07
 	Loop                      Atom = 0x5d04
 	Low                       Atom = 0xc303
 	Main                      Atom = 0x1004
 	Malignmark                Atom = 0xb00a
-	Manifest                  Atom = 0x6d708
-	Map                       Atom = 0x31803
+	Manifest                  Atom = 0x6d508
+	Map                       Atom = 0x30703
 	Mark                      Atom = 0xb604
-	Marquee                   Atom = 0x32707
-	Math                      Atom = 0x32e04
-	Max                       Atom = 0x33d03
-	Maxlength                 Atom = 0x33d09
+	Marquee                   Atom = 0x31607
+	Math                      Atom = 0x31d04
+	Max                       Atom = 0x33703
+	Maxlength                 Atom = 0x33709
 	Media                     Atom = 0xe605
 	Mediagroup                Atom = 0xe60a
-	Menu                      Atom = 0x38704
-	Menuitem                  Atom = 0x38708
-	Meta                      Atom = 0x4b804
+	Menu                      Atom = 0x38104
+	Menuitem                  Atom = 0x38108
+	Meta                      Atom = 0x4ac04
 	Meter                     Atom = 0x9805
-	Method                    Atom = 0x2a806
-	Mglyph                    Atom = 0x30806
-	Mi                        Atom = 0x34702
-	Min                       Atom = 0x34703
-	Minlength                 Atom = 0x34709
-	Mn                        Atom = 0x2b102
+	Method                    Atom = 0x29b06
+	Mglyph                    Atom = 0x2f706
+	Mi                        Atom = 0x34102
+	Min                       Atom = 0x34103
+	Minlength                 Atom = 0x34109
+	Mn                        Atom = 0x2a402
 	Mo                        Atom = 0xa402
-	Ms                        Atom = 0x67402
-	Mtext                     Atom = 0x35105
-	Multiple                  Atom = 0x35f08
-	Muted                     Atom = 0x36705
+	Ms                        Atom = 0x67202
+	Mtext                     Atom = 0x34b05
+	Multiple                  Atom = 0x35908
+	Muted                     Atom = 0x36105
 	Name                      Atom = 0x9604
 	Nav                       Atom = 0x1303
 	Nobr                      Atom = 0x3704
@@ -185,101 +185,101 @@ const (
 	Noframes                  Atom = 0x8908
 	Nomodule                  Atom = 0xa208
 	Nonce                     Atom = 0x1a605
-	Noscript                  Atom = 0x21608
-	Novalidate                Atom = 0x2b20a
-	Object                    Atom = 0x26806
+	Noscript                  Atom = 0x2c208
+	Novalidate                Atom = 0x2a50a
+	Object                    Atom = 0x25b06
 	Ol                        Atom = 0x13702
 	Onabort                   Atom = 0x19507
-	Onafterprint              Atom = 0x2360c
-	Onautocomplete            Atom = 0x2760e
-	Onautocompleteerror       Atom = 0x27613
-	Onauxclick                Atom = 0x61f0a
-	Onbeforeprint             Atom = 0x69e0d
-	Onbeforeunload            Atom = 0x6e70e
-	Onblur                    Atom = 0x56d06
+	Onafterprint              Atom = 0x2290c
+	Onautocomplete            Atom = 0x2690e
+	Onautocompleteerror       Atom = 0x26913
+	Onauxclick                Atom = 0x6140a
+	Onbeforeprint             Atom = 0x69c0d
+	Onbeforeunload            Atom = 0x6e50e
+	Onblur                    Atom = 0x1ea06
 	Oncancel                  Atom = 0x11908
 	Oncanplay                 Atom = 0x14d09
 	Oncanplaythrough          Atom = 0x14d10
-	Onchange                  Atom = 0x41b08
-	Onclick                   Atom = 0x2f507
-	Onclose                   Atom = 0x36c07
-	Oncontextmenu             Atom = 0x37e0d
-	Oncopy                    Atom = 0x39106
-	Oncuechange               Atom = 0x3970b
-	Oncut                     Atom = 0x3a205
-	Ondblclick                Atom = 0x3a70a
-	Ondrag                    Atom = 0x3b106
-	Ondragend                 Atom = 0x3b109
-	Ondragenter               Atom = 0x3ba0b
-	Ondragexit                Atom = 0x3c50a
-	Ondragleave               Atom = 0x3df0b
-	Ondragover                Atom = 0x3ea0a
-	Ondragstart               Atom = 0x3f40b
-	Ondrop                    Atom = 0x40306
-	Ondurationchange          Atom = 0x41310
-	Onemptied                 Atom = 0x40a09
-	Onended                   Atom = 0x42307
-	Onerror                   Atom = 0x42a07
-	Onfocus                   Atom = 0x43107
-	Onhashchange              Atom = 0x43d0c
-	Oninput                   Atom = 0x44907
-	Oninvalid                 Atom = 0x45509
-	Onkeydown                 Atom = 0x45e09
-	Onkeypress                Atom = 0x46b0a
-	Onkeyup                   Atom = 0x48007
-	Onlanguagechange          Atom = 0x48d10
-	Onload                    Atom = 0x49d06
-	Onloadeddata              Atom = 0x49d0c
-	Onloadedmetadata          Atom = 0x4b010
-	Onloadend                 Atom = 0x4c609
-	Onloadstart               Atom = 0x4cf0b
-	Onmessage                 Atom = 0x4da09
-	Onmessageerror            Atom = 0x4da0e
-	Onmousedown               Atom = 0x4e80b
-	Onmouseenter              Atom = 0x4f30c
-	Onmouseleave              Atom = 0x4ff0c
-	Onmousemove               Atom = 0x50b0b
-	Onmouseout                Atom = 0x5160a
-	Onmouseover               Atom = 0x5230b
-	Onmouseup                 Atom = 0x52e09
-	Onmousewheel              Atom = 0x53c0c
-	Onoffline                 Atom = 0x54809
-	Ononline                  Atom = 0x55108
-	Onpagehide                Atom = 0x5590a
-	Onpageshow                Atom = 0x5730a
-	Onpaste                   Atom = 0x57f07
-	Onpause                   Atom = 0x59a07
-	Onplay                    Atom = 0x5a406
-	Onplaying                 Atom = 0x5a409
-	Onpopstate                Atom = 0x5ad0a
-	Onprogress                Atom = 0x5b70a
-	Onratechange              Atom = 0x5cc0c
-	Onrejectionhandled        Atom = 0x5d812
-	Onreset                   Atom = 0x5ea07
-	Onresize                  Atom = 0x5f108
-	Onscroll                  Atom = 0x60008
-	Onsecuritypolicyviolation Atom = 0x60819
-	Onseeked                  Atom = 0x62908
-	Onseeking                 Atom = 0x63109
-	Onselect                  Atom = 0x63a08
-	Onshow                    Atom = 0x64406
-	Onsort                    Atom = 0x64f06
-	Onstalled                 Atom = 0x65909
-	Onstorage                 Atom = 0x66209
-	Onsubmit                  Atom = 0x66b08
-	Onsuspend                 Atom = 0x67b09
+	Onchange                  Atom = 0x41508
+	Onclick                   Atom = 0x2e407
+	Onclose                   Atom = 0x36607
+	Oncontextmenu             Atom = 0x3780d
+	Oncopy                    Atom = 0x38b06
+	Oncuechange               Atom = 0x3910b
+	Oncut                     Atom = 0x39c05
+	Ondblclick                Atom = 0x3a10a
+	Ondrag                    Atom = 0x3ab06
+	Ondragend                 Atom = 0x3ab09
+	Ondragenter               Atom = 0x3b40b
+	Ondragexit                Atom = 0x3bf0a
+	Ondragleave               Atom = 0x3d90b
+	Ondragover                Atom = 0x3e40a
+	Ondragstart               Atom = 0x3ee0b
+	Ondrop                    Atom = 0x3fd06
+	Ondurationchange          Atom = 0x40d10
+	Onemptied                 Atom = 0x40409
+	Onended                   Atom = 0x41d07
+	Onerror                   Atom = 0x42407
+	Onfocus                   Atom = 0x42b07
+	Onhashchange              Atom = 0x4370c
+	Oninput                   Atom = 0x44307
+	Oninvalid                 Atom = 0x44f09
+	Onkeydown                 Atom = 0x45809
+	Onkeypress                Atom = 0x4650a
+	Onkeyup                   Atom = 0x47407
+	Onlanguagechange          Atom = 0x48110
+	Onload                    Atom = 0x49106
+	Onloadeddata              Atom = 0x4910c
+	Onloadedmetadata          Atom = 0x4a410
+	Onloadend                 Atom = 0x4ba09
+	Onloadstart               Atom = 0x4c30b
+	Onmessage                 Atom = 0x4ce09
+	Onmessageerror            Atom = 0x4ce0e
+	Onmousedown               Atom = 0x4dc0b
+	Onmouseenter              Atom = 0x4e70c
+	Onmouseleave              Atom = 0x4f30c
+	Onmousemove               Atom = 0x4ff0b
+	Onmouseout                Atom = 0x50a0a
+	Onmouseover               Atom = 0x5170b
+	Onmouseup                 Atom = 0x52209
+	Onmousewheel              Atom = 0x5300c
+	Onoffline                 Atom = 0x53c09
+	Ononline                  Atom = 0x54508
+	Onpagehide                Atom = 0x54d0a
+	Onpageshow                Atom = 0x5630a
+	Onpaste                   Atom = 0x56f07
+	Onpause                   Atom = 0x58a07
+	Onplay                    Atom = 0x59406
+	Onplaying                 Atom = 0x59409
+	Onpopstate                Atom = 0x59d0a
+	Onprogress                Atom = 0x5a70a
+	Onratechange              Atom = 0x5bc0c
+	Onrejectionhandled        Atom = 0x5c812
+	Onreset                   Atom = 0x5da07
+	Onresize                  Atom = 0x5e108
+	Onscroll                  Atom = 0x5f508
+	Onsecuritypolicyviolation Atom = 0x5fd19
+	Onseeked                  Atom = 0x61e08
+	Onseeking                 Atom = 0x62609
+	Onselect                  Atom = 0x62f08
+	Onshow                    Atom = 0x63906
+	Onsort                    Atom = 0x64d06
+	Onstalled                 Atom = 0x65709
+	Onstorage                 Atom = 0x66009
+	Onsubmit                  Atom = 0x66908
+	Onsuspend                 Atom = 0x67909
 	Ontimeupdate              Atom = 0x400c
-	Ontoggle                  Atom = 0x68408
-	Onunhandledrejection      Atom = 0x68c14
-	Onunload                  Atom = 0x6ab08
-	Onvolumechange            Atom = 0x6b30e
-	Onwaiting                 Atom = 0x6c109
-	Onwheel                   Atom = 0x6ca07
+	Ontoggle                  Atom = 0x68208
+	Onunhandledrejection      Atom = 0x68a14
+	Onunload                  Atom = 0x6a908
+	Onvolumechange            Atom = 0x6b10e
+	Onwaiting                 Atom = 0x6bf09
+	Onwheel                   Atom = 0x6c807
 	Open                      Atom = 0x1a304
 	Optgroup                  Atom = 0x5f08
-	Optimum                   Atom = 0x6d107
-	Option                    Atom = 0x6e306
-	Output                    Atom = 0x51d06
+	Optimum                   Atom = 0x6cf07
+	Option                    Atom = 0x6e106
+	Output                    Atom = 0x51106
 	P                         Atom = 0xc01
 	Param                     Atom = 0xc05
 	Pattern                   Atom = 0x6607
@@ -288,466 +288,468 @@ const (
 	Placeholder               Atom = 0x1310b
 	Plaintext                 Atom = 0x1b209
 	Playsinline               Atom = 0x1400b
-	Poster                    Atom = 0x2cf06
-	Pre                       Atom = 0x47003
-	Preload                   Atom = 0x48607
-	Progress                  Atom = 0x5b908
-	Prompt                    Atom = 0x53606
-	Public                    Atom = 0x58606
+	Poster                    Atom = 0x64706
+	Pre                       Atom = 0x46a03
+	Preload                   Atom = 0x47a07
+	Progress                  Atom = 0x5a908
+	Prompt                    Atom = 0x52a06
+	Public                    Atom = 0x57606
 	Q                         Atom = 0xcf01
 	Radiogroup                Atom = 0x30a
 	Rb                        Atom = 0x3a02
-	Readonly                  Atom = 0x35708
-	Referrerpolicy            Atom = 0x3d10e
-	Rel                       Atom = 0x48703
-	Required                  Atom = 0x24c08
+	Readonly                  Atom = 0x35108
+	Referrerpolicy            Atom = 0x3cb0e
+	Rel                       Atom = 0x47b03
+	Required                  Atom = 0x23f08
 	Reversed                  Atom = 0x8008
 	Rows                      Atom = 0x9c04
 	Rowspan                   Atom = 0x9c07
-	Rp                        Atom = 0x23c02
+	Rp                        Atom = 0x22f02
 	Rt                        Atom = 0x19a02
 	Rtc                       Atom = 0x19a03
 	Ruby                      Atom = 0xfb04
 	S                         Atom = 0x2501
 	Samp                      Atom = 0x7804
 	Sandbox                   Atom = 0x12907
-	Scope                     Atom = 0x67505
-	Scoped                    Atom = 0x67506
-	Script                    Atom = 0x21806
-	Seamless                  Atom = 0x37108
-	Section                   Atom = 0x56807
-	Select                    Atom = 0x63c06
-	Selected                  Atom = 0x63c08
-	Shape                     Atom = 0x1e505
-	Size                      Atom = 0x5f504
-	Sizes                     Atom = 0x5f505
-	Slot                      Atom = 0x1ef04
-	Small                     Atom = 0x20605
-	Sortable                  Atom = 0x65108
-	Sorted                    Atom = 0x33706
-	Source                    Atom = 0x37806
-	Spacer                    Atom = 0x43706
+	Scope                     Atom = 0x67305
+	Scoped                    Atom = 0x67306
+	Script                    Atom = 0x2c406
+	Seamless                  Atom = 0x36b08
+	Search                    Atom = 0x55c06
+	Section                   Atom = 0x1e507
+	Select                    Atom = 0x63106
+	Selected                  Atom = 0x63108
+	Shape                     Atom = 0x1f505
+	Size                      Atom = 0x5e504
+	Sizes                     Atom = 0x5e505
+	Slot                      Atom = 0x20504
+	Small                     Atom = 0x32605
+	Sortable                  Atom = 0x64f08
+	Sorted                    Atom = 0x37206
+	Source                    Atom = 0x43106
+	Spacer                    Atom = 0x46e06
 	Span                      Atom = 0x9f04
-	Spellcheck                Atom = 0x4740a
-	Src                       Atom = 0x5c003
-	Srcdoc                    Atom = 0x5c006
-	Srclang                   Atom = 0x5f907
-	Srcset                    Atom = 0x6f906
-	Start                     Atom = 0x3fa05
-	Step                      Atom = 0x58304
+	Spellcheck                Atom = 0x5b00a
+	Src                       Atom = 0x5e903
+	Srcdoc                    Atom = 0x5e906
+	Srclang                   Atom = 0x6f707
+	Srcset                    Atom = 0x6fe06
+	Start                     Atom = 0x3f405
+	Step                      Atom = 0x57304
 	Strike                    Atom = 0xd206
-	Strong                    Atom = 0x6dd06
-	Style                     Atom = 0x6ff05
-	Sub                       Atom = 0x66d03
-	Summary                   Atom = 0x70407
-	Sup                       Atom = 0x70b03
-	Svg                       Atom = 0x70e03
-	System                    Atom = 0x71106
-	Tabindex                  Atom = 0x4be08
-	Table                     Atom = 0x59505
-	Target                    Atom = 0x2c406
+	Strong                    Atom = 0x6db06
+	Style                     Atom = 0x70405
+	Sub                       Atom = 0x66b03
+	Summary                   Atom = 0x70907
+	Sup                       Atom = 0x71003
+	Svg                       Atom = 0x71303
+	System                    Atom = 0x71606
+	Tabindex                  Atom = 0x4b208
+	Table                     Atom = 0x58505
+	Target                    Atom = 0x2b706
 	Tbody                     Atom = 0x2705
 	Td                        Atom = 0x9202
-	Template                  Atom = 0x71408
-	Textarea                  Atom = 0x35208
+	Template                  Atom = 0x71908
+	Textarea                  Atom = 0x34c08
 	Tfoot                     Atom = 0xf505
 	Th                        Atom = 0x15602
-	Thead                     Atom = 0x33005
+	Thead                     Atom = 0x31f05
 	Time                      Atom = 0x4204
 	Title                     Atom = 0x11005
 	Tr                        Atom = 0xcc02
 	Track                     Atom = 0x1ba05
-	Translate                 Atom = 0x1f209
+	Translate                 Atom = 0x20809
 	Tt                        Atom = 0x6802
 	Type                      Atom = 0xd904
-	Typemustmatch             Atom = 0x2900d
+	Typemustmatch             Atom = 0x2830d
 	U                         Atom = 0xb01
 	Ul                        Atom = 0xa702
 	Updateviacache            Atom = 0x460e
-	Usemap                    Atom = 0x59e06
+	Usemap                    Atom = 0x58e06
 	Value                     Atom = 0x1505
 	Var                       Atom = 0x16d03
-	Video                     Atom = 0x2f105
-	Wbr                       Atom = 0x57c03
-	Width                     Atom = 0x64905
-	Workertype                Atom = 0x71c0a
-	Wrap                      Atom = 0x72604
+	Video                     Atom = 0x2e005
+	Wbr                       Atom = 0x56c03
+	Width                     Atom = 0x63e05
+	Workertype                Atom = 0x7210a
+	Wrap                      Atom = 0x72b04
 	Xmp                       Atom = 0x12f03
 )
 
-const hash0 = 0x81cdf10e
+const hash0 = 0x84f70e16
 
 const maxAtomLen = 25
 
 var table = [1 << 9]Atom{
-	0x1:   0xe60a,  // mediagroup
-	0x2:   0x2e404, // lang
-	0x4:   0x2c09,  // accesskey
-	0x5:   0x8b08,  // frameset
-	0x7:   0x63a08, // onselect
-	0x8:   0x71106, // system
-	0xa:   0x64905, // width
-	0xc:   0x2890b, // formenctype
-	0xd:   0x13702, // ol
-	0xe:   0x3970b, // oncuechange
-	0x10:  0x14b03, // bdo
-	0x11:  0x11505, // audio
-	0x12:  0x17a09, // draggable
-	0x14:  0x2f105, // video
-	0x15:  0x2b102, // mn
-	0x16:  0x38704, // menu
-	0x17:  0x2cf06, // poster
-	0x19:  0xf606,  // footer
-	0x1a:  0x2a806, // method
-	0x1b:  0x2b808, // datetime
-	0x1c:  0x19507, // onabort
-	0x1d:  0x460e,  // updateviacache
-	0x1e:  0xff05,  // async
-	0x1f:  0x49d06, // onload
-	0x21:  0x11908, // oncancel
-	0x22:  0x62908, // onseeked
-	0x23:  0x30205, // image
-	0x24:  0x5d812, // onrejectionhandled
-	0x26:  0x17404, // link
-	0x27:  0x51d06, // output
-	0x28:  0x33104, // head
-	0x29:  0x4ff0c, // onmouseleave
-	0x2a:  0x57f07, // onpaste
-	0x2b:  0x5a409, // onplaying
-	0x2c:  0x1c407, // colspan
-	0x2f:  0x1bf05, // color
-	0x30:  0x5f504, // size
-	0x31:  0x2e80a, // http-equiv
-	0x33:  0x601,   // i
-	0x34:  0x5590a, // onpagehide
-	0x35:  0x68c14, // onunhandledrejection
-	0x37:  0x42a07, // onerror
-	0x3a:  0x3b08,  // basefont
-	0x3f:  0x1303,  // nav
-	0x40:  0x17704, // kind
-	0x41:  0x35708, // readonly
-	0x42:  0x30806, // mglyph
-	0x44:  0xb202,  // li
-	0x46:  0x2d506, // hidden
-	0x47:  0x70e03, // svg
-	0x48:  0x58304, // step
-	0x49:  0x23f09, // integrity
-	0x4a:  0x58606, // public
-	0x4c:  0x1ab03, // col
-	0x4d:  0x1870a, // blockquote
-	0x4e:  0x34f02, // h5
-	0x50:  0x5b908, // progress
-	0x51:  0x5f505, // sizes
-	0x52:  0x34502, // h4
-	0x56:  0x33005, // thead
-	0x57:  0xd607,  // keytype
-	0x58:  0x5b70a, // onprogress
-	0x59:  0x44b09, // inputmode
-	0x5a:  0x3b109, // ondragend
-	0x5d:  0x3a205, // oncut
-	0x5e:  0x43706, // spacer
-	0x5f:  0x1ab08, // colgroup
-	0x62:  0x16502, // is
-	0x65:  0x3c02,  // as
-	0x66:  0x54809, // onoffline
-	0x67:  0x33706, // sorted
-	0x69:  0x48d10, // onlanguagechange
-	0x6c:  0x43d0c, // onhashchange
-	0x6d:  0x9604,  // name
-	0x6e:  0xf505,  // tfoot
-	0x6f:  0x56104, // desc
-	0x70:  0x33d03, // max
-	0x72:  0x1ea06, // coords
-	0x73:  0x30d02, // h3
-	0x74:  0x6e70e, // onbeforeunload
-	0x75:  0x9c04,  // rows
-	0x76:  0x63c06, // select
-	0x77:  0x9805,  // meter
-	0x78:  0x38b06, // itemid
-	0x79:  0x53c0c, // onmousewheel
-	0x7a:  0x5c006, // srcdoc
-	0x7d:  0x1ba05, // track
-	0x7f:  0x31f08, // itemtype
-	0x82:  0xa402,  // mo
-	0x83:  0x41b08, // onchange
-	0x84:  0x33107, // headers
-	0x85:  0x5cc0c, // onratechange
-	0x86:  0x60819, // onsecuritypolicyviolation
-	0x88:  0x4a508, // datalist
-	0x89:  0x4e80b, // onmousedown
-	0x8a:  0x1ef04, // slot
-	0x8b:  0x4b010, // onloadedmetadata
-	0x8c:  0x1a06,  // accept
-	0x8d:  0x26806, // object
-	0x91:  0x6b30e, // onvolumechange
-	0x92:  0x2107,  // charset
-	0x93:  0x27613, // onautocompleteerror
-	0x94:  0xc113,  // allowpaymentrequest
-	0x95:  0x2804,  // body
-	0x96:  0x10a07, // default
-	0x97:  0x63c08, // selected
-	0x98:  0x21e04, // face
-	0x99:  0x1e505, // shape
-	0x9b:  0x68408, // ontoggle
-	0x9e:  0x64b02, // dt
-	0x9f:  0xb604,  // mark
-	0xa1:  0xb01,   // u
-	0xa4:  0x6ab08, // onunload
-	0xa5:  0x5d04,  // loop
-	0xa6:  0x16408, // disabled
-	0xaa:  0x42307, // onended
-	0xab:  0xb00a,  // malignmark
-	0xad:  0x67b09, // onsuspend
-	0xae:  0x35105, // mtext
-	0xaf:  0x64f06, // onsort
-	0xb0:  0x19d08, // itemprop
-	0xb3:  0x67109, // itemscope
-	0xb4:  0x17305, // blink
-	0xb6:  0x3b106, // ondrag
-	0xb7:  0xa702,  // ul
-	0xb8:  0x26e04, // form
-	0xb9:  0x12907, // sandbox
-	0xba:  0x8b05,  // frame
-	0xbb:  0x1505,  // value
-	0xbc:  0x66209, // onstorage
-	0xbf:  0xaa07,  // acronym
-	0xc0:  0x19a02, // rt
-	0xc2:  0x202,   // br
-	0xc3:  0x22608, // fieldset
-	0xc4:  0x2900d, // typemustmatch
-	0xc5:  0xa208,  // nomodule
-	0xc6:  0x6c07,  // noembed
-	0xc7:  0x69e0d, // onbeforeprint
-	0xc8:  0x19106, // button
-	0xc9:  0x2f507, // onclick
-	0xca:  0x70407, // summary
-	0xcd:  0xfb04,  // ruby
-	0xce:  0x56405, // class
-	0xcf:  0x3f40b, // ondragstart
-	0xd0:  0x23107, // caption
-	0xd4:  0xdd0e,  // allowusermedia
-	0xd5:  0x4cf0b, // onloadstart
-	0xd9:  0x16b03, // div
-	0xda:  0x4a904, // list
-	0xdb:  0x32e04, // math
-	0xdc:  0x44b05, // input
-	0xdf:  0x3ea0a, // ondragover
-	0xe0:  0x2de02, // h2
-	0xe2:  0x1b209, // plaintext
-	0xe4:  0x4f30c, // onmouseenter
-	0xe7:  0x47907, // checked
-	0xe8:  0x47003, // pre
-	0xea:  0x35f08, // multiple
-	0xeb:  0xba03,  // bdi
-	0xec:  0x33d09, // maxlength
-	0xed:  0xcf01,  // q
-	0xee:  0x61f0a, // onauxclick
-	0xf0:  0x57c03, // wbr
-	0xf2:  0x3b04,  // base
-	0xf3:  0x6e306, // option
-	0xf5:  0x41310, // ondurationchange
-	0xf7:  0x8908,  // noframes
-	0xf9:  0x40508, // dropzone
-	0xfb:  0x67505, // scope
-	0xfc:  0x8008,  // reversed
-	0xfd:  0x3ba0b, // ondragenter
-	0xfe:  0x3fa05, // start
-	0xff:  0x12f03, // xmp
-	0x100: 0x5f907, // srclang
-	0x101: 0x30703, // img
-	0x104: 0x101,   // b
-	0x105: 0x25403, // for
-	0x106: 0x10705, // aside
-	0x107: 0x44907, // oninput
-	0x108: 0x35604, // area
-	0x109: 0x2a40a, // formmethod
-	0x10a: 0x72604, // wrap
-	0x10c: 0x23c02, // rp
-	0x10d: 0x46b0a, // onkeypress
-	0x10e: 0x6802,  // tt
-	0x110: 0x34702, // mi
-	0x111: 0x36705, // muted
-	0x112: 0xf303,  // alt
-	0x113: 0x5c504, // code
-	0x114: 0x6e02,  // em
-	0x115: 0x3c50a, // ondragexit
-	0x117: 0x9f04,  // span
-	0x119: 0x6d708, // manifest
-	0x11a: 0x38708, // menuitem
-	0x11b: 0x58b07, // content
-	0x11d: 0x6c109, // onwaiting
-	0x11f: 0x4c609, // onloadend
-	0x121: 0x37e0d, // oncontextmenu
-	0x123: 0x56d06, // onblur
-	0x124: 0x3fc07, // article
-	0x125: 0x9303,  // dir
-	0x126: 0xef04,  // ping
-	0x127: 0x24c08, // required
-	0x128: 0x45509, // oninvalid
-	0x129: 0xb105,  // align
-	0x12b: 0x58a04, // icon
-	0x12c: 0x64d02, // h6
-	0x12d: 0x1c404, // cols
-	0x12e: 0x22e0a, // figcaption
-	0x12f: 0x45e09, // onkeydown
-	0x130: 0x66b08, // onsubmit
-	0x131: 0x14d09, // oncanplay
-	0x132: 0x70b03, // sup
-	0x133: 0xc01,   // p
-	0x135: 0x40a09, // onemptied
-	0x136: 0x39106, // oncopy
-	0x137: 0x19c04, // cite
-	0x138: 0x3a70a, // ondblclick
-	0x13a: 0x50b0b, // onmousemove
-	0x13c: 0x66d03, // sub
-	0x13d: 0x48703, // rel
-	0x13e: 0x5f08,  // optgroup
-	0x142: 0x9c07,  // rowspan
-	0x143: 0x37806, // source
-	0x144: 0x21608, // noscript
-	0x145: 0x1a304, // open
-	0x146: 0x20403, // ins
-	0x147: 0x2540d, // foreignObject
-	0x148: 0x5ad0a, // onpopstate
-	0x14a: 0x28d07, // enctype
-	0x14b: 0x2760e, // onautocomplete
-	0x14c: 0x35208, // textarea
-	0x14e: 0x2780c, // autocomplete
-	0x14f: 0x15702, // hr
-	0x150: 0x1de08, // controls
-	0x151: 0x10902, // id
-	0x153: 0x2360c, // onafterprint
-	0x155: 0x2610d, // foreignobject
-	0x156: 0x32707, // marquee
-	0x157: 0x59a07, // onpause
-	0x158: 0x5e602, // dl
-	0x159: 0x5206,  // height
-	0x15a: 0x34703, // min
-	0x15b: 0x9307,  // dirname
-	0x15c: 0x1f209, // translate
-	0x15d: 0x5604,  // html
-	0x15e: 0x34709, // minlength
-	0x15f: 0x48607, // preload
-	0x160: 0x71408, // template
-	0x161: 0x3df0b, // ondragleave
-	0x162: 0x3a02,  // rb
-	0x164: 0x5c003, // src
-	0x165: 0x6dd06, // strong
-	0x167: 0x7804,  // samp
-	0x168: 0x6f307, // address
-	0x169: 0x55108, // ononline
-	0x16b: 0x1310b, // placeholder
-	0x16c: 0x2c406, // target
-	0x16d: 0x20605, // small
-	0x16e: 0x6ca07, // onwheel
-	0x16f: 0x1c90a, // annotation
-	0x170: 0x4740a, // spellcheck
-	0x171: 0x7207,  // details
-	0x172: 0x10306, // canvas
-	0x173: 0x12109, // autofocus
-	0x174: 0xc05,   // param
-	0x176: 0x46308, // download
-	0x177: 0x45203, // del
-	0x178: 0x36c07, // onclose
-	0x179: 0xb903,  // kbd
-	0x17a: 0x31906, // applet
-	0x17b: 0x2e004, // href
-	0x17c: 0x5f108, // onresize
-	0x17e: 0x49d0c, // onloadeddata
-	0x180: 0xcc02,  // tr
-	0x181: 0x2c00a, // formtarget
-	0x182: 0x11005, // title
-	0x183: 0x6ff05, // style
-	0x184: 0xd206,  // strike
-	0x185: 0x59e06, // usemap
-	0x186: 0x2fc06, // iframe
-	0x187: 0x1004,  // main
-	0x189: 0x7b07,  // picture
-	0x18c: 0x31605, // ismap
-	0x18e: 0x4a504, // data
-	0x18f: 0x5905,  // label
-	0x191: 0x3d10e, // referrerpolicy
-	0x192: 0x15602, // th
-	0x194: 0x53606, // prompt
-	0x195: 0x56807, // section
-	0x197: 0x6d107, // optimum
-	0x198: 0x2db04, // high
-	0x199: 0x15c02, // h1
-	0x19a: 0x65909, // onstalled
-	0x19b: 0x16d03, // var
-	0x19c: 0x4204,  // time
-	0x19e: 0x67402, // ms
-	0x19f: 0x33106, // header
-	0x1a0: 0x4da09, // onmessage
-	0x1a1: 0x1a605, // nonce
-	0x1a2: 0x26e0a, // formaction
-	0x1a3: 0x22006, // center
-	0x1a4: 0x3704,  // nobr
-	0x1a5: 0x59505, // table
-	0x1a6: 0x4a907, // listing
-	0x1a7: 0x18106, // legend
-	0x1a9: 0x29b09, // challenge
-	0x1aa: 0x24806, // figure
-	0x1ab: 0xe605,  // media
-	0x1ae: 0xd904,  // type
-	0x1af: 0x3f04,  // font
-	0x1b0: 0x4da0e, // onmessageerror
-	0x1b1: 0x37108, // seamless
-	0x1b2: 0x8703,  // dfn
-	0x1b3: 0x5c705, // defer
-	0x1b4: 0xc303,  // low
-	0x1b5: 0x19a03, // rtc
-	0x1b6: 0x5230b, // onmouseover
-	0x1b7: 0x2b20a, // novalidate
-	0x1b8: 0x71c0a, // workertype
-	0x1ba: 0x3cd07, // itemref
-	0x1bd: 0x1,     // a
-	0x1be: 0x31803, // map
-	0x1bf: 0x400c,  // ontimeupdate
-	0x1c0: 0x15e07, // bgsound
-	0x1c1: 0x3206,  // keygen
-	0x1c2: 0x2705,  // tbody
-	0x1c5: 0x64406, // onshow
-	0x1c7: 0x2501,  // s
-	0x1c8: 0x6607,  // pattern
-	0x1cc: 0x14d10, // oncanplaythrough
-	0x1ce: 0x2d702, // dd
-	0x1cf: 0x6f906, // srcset
-	0x1d0: 0x17003, // big
-	0x1d2: 0x65108, // sortable
-	0x1d3: 0x48007, // onkeyup
-	0x1d5: 0x5a406, // onplay
-	0x1d7: 0x4b804, // meta
-	0x1d8: 0x40306, // ondrop
-	0x1da: 0x60008, // onscroll
-	0x1db: 0x1fb0b, // crossorigin
-	0x1dc: 0x5730a, // onpageshow
-	0x1dd: 0x4,     // abbr
-	0x1de: 0x9202,  // td
-	0x1df: 0x58b0f, // contenteditable
-	0x1e0: 0x27206, // action
-	0x1e1: 0x1400b, // playsinline
-	0x1e2: 0x43107, // onfocus
-	0x1e3: 0x2e008, // hreflang
-	0x1e5: 0x5160a, // onmouseout
-	0x1e6: 0x5ea07, // onreset
-	0x1e7: 0x13c08, // autoplay
-	0x1e8: 0x63109, // onseeking
-	0x1ea: 0x67506, // scoped
-	0x1ec: 0x30a,   // radiogroup
-	0x1ee: 0x3800b, // contextmenu
-	0x1ef: 0x52e09, // onmouseup
-	0x1f1: 0x2ca06, // hgroup
-	0x1f2: 0x2080f, // allowfullscreen
-	0x1f3: 0x4be08, // tabindex
-	0x1f6: 0x30f07, // isindex
-	0x1f7: 0x1a0e,  // accept-charset
-	0x1f8: 0x2ae0e, // formnovalidate
-	0x1fb: 0x1c90e, // annotation-xml
-	0x1fc: 0x6e05,  // embed
-	0x1fd: 0x21806, // script
-	0x1fe: 0xbb06,  // dialog
-	0x1ff: 0x1d707, // command
+	0x1:   0x3ff08, // dropzone
+	0x2:   0x3b08,  // basefont
+	0x3:   0x23209, // integrity
+	0x4:   0x43106, // source
+	0x5:   0x2c09,  // accesskey
+	0x6:   0x1a06,  // accept
+	0x7:   0x6c807, // onwheel
+	0xb:   0x47407, // onkeyup
+	0xc:   0x32007, // headers
+	0xd:   0x67306, // scoped
+	0xe:   0x67909, // onsuspend
+	0xf:   0x8908,  // noframes
+	0x10:  0x1fa0b, // crossorigin
+	0x11:  0x2e407, // onclick
+	0x12:  0x3f405, // start
+	0x13:  0x37a0b, // contextmenu
+	0x14:  0x5e903, // src
+	0x15:  0x1c404, // cols
+	0x16:  0xbb06,  // dialog
+	0x17:  0x47a07, // preload
+	0x18:  0x3c707, // itemref
+	0x1b:  0x2f105, // image
+	0x1d:  0x4ba09, // onloadend
+	0x1e:  0x45d08, // download
+	0x1f:  0x46a03, // pre
+	0x23:  0x2970a, // formmethod
+	0x24:  0x71303, // svg
+	0x25:  0xcf01,  // q
+	0x26:  0x64002, // dt
+	0x27:  0x1de08, // controls
+	0x2a:  0x2804,  // body
+	0x2b:  0xd206,  // strike
+	0x2c:  0x3910b, // oncuechange
+	0x2d:  0x4c30b, // onloadstart
+	0x2e:  0x2fe07, // isindex
+	0x2f:  0xb202,  // li
+	0x30:  0x1400b, // playsinline
+	0x31:  0x34102, // mi
+	0x32:  0x30806, // applet
+	0x33:  0x4ce09, // onmessage
+	0x35:  0x13702, // ol
+	0x36:  0x1a304, // open
+	0x39:  0x14d09, // oncanplay
+	0x3a:  0x6bf09, // onwaiting
+	0x3b:  0x11908, // oncancel
+	0x3c:  0x6a908, // onunload
+	0x3e:  0x53c09, // onoffline
+	0x3f:  0x1a0e,  // accept-charset
+	0x40:  0x32004, // head
+	0x42:  0x3ab09, // ondragend
+	0x43:  0x1310b, // placeholder
+	0x44:  0x2b30a, // formtarget
+	0x45:  0x2540d, // foreignobject
+	0x47:  0x400c,  // ontimeupdate
+	0x48:  0xdd0e,  // allowusermedia
+	0x4a:  0x69c0d, // onbeforeprint
+	0x4b:  0x5604,  // html
+	0x4c:  0x9f04,  // span
+	0x4d:  0x64206, // hgroup
+	0x4e:  0x16408, // disabled
+	0x4f:  0x4204,  // time
+	0x51:  0x42b07, // onfocus
+	0x53:  0xb00a,  // malignmark
+	0x55:  0x4650a, // onkeypress
+	0x56:  0x55805, // class
+	0x57:  0x1ab08, // colgroup
+	0x58:  0x33709, // maxlength
+	0x59:  0x5a908, // progress
+	0x5b:  0x70405, // style
+	0x5c:  0x2a10e, // formnovalidate
+	0x5e:  0x38b06, // oncopy
+	0x60:  0x26104, // form
+	0x61:  0xf606,  // footer
+	0x64:  0x30a,   // radiogroup
+	0x66:  0xfb04,  // ruby
+	0x67:  0x4ff0b, // onmousemove
+	0x68:  0x19d08, // itemprop
+	0x69:  0x2d70a, // http-equiv
+	0x6a:  0x15602, // th
+	0x6c:  0x6e02,  // em
+	0x6d:  0x38108, // menuitem
+	0x6e:  0x63106, // select
+	0x6f:  0x48110, // onlanguagechange
+	0x70:  0x31f05, // thead
+	0x71:  0x15c02, // h1
+	0x72:  0x5e906, // srcdoc
+	0x75:  0x9604,  // name
+	0x76:  0x19106, // button
+	0x77:  0x55504, // desc
+	0x78:  0x17704, // kind
+	0x79:  0x1bf05, // color
+	0x7c:  0x58e06, // usemap
+	0x7d:  0x30e08, // itemtype
+	0x7f:  0x6d508, // manifest
+	0x81:  0x5300c, // onmousewheel
+	0x82:  0x4dc0b, // onmousedown
+	0x84:  0xc05,   // param
+	0x85:  0x2e005, // video
+	0x86:  0x4910c, // onloadeddata
+	0x87:  0x6f107, // address
+	0x8c:  0xef04,  // ping
+	0x8d:  0x24703, // for
+	0x8f:  0x62f08, // onselect
+	0x90:  0x30703, // map
+	0x92:  0xc01,   // p
+	0x93:  0x8008,  // reversed
+	0x94:  0x54d0a, // onpagehide
+	0x95:  0x3206,  // keygen
+	0x96:  0x34109, // minlength
+	0x97:  0x3e40a, // ondragover
+	0x98:  0x42407, // onerror
+	0x9a:  0x2107,  // charset
+	0x9b:  0x29b06, // method
+	0x9c:  0x101,   // b
+	0x9d:  0x68208, // ontoggle
+	0x9e:  0x2bd06, // hidden
+	0xa0:  0x3f607, // article
+	0xa2:  0x63906, // onshow
+	0xa3:  0x64d06, // onsort
+	0xa5:  0x57b0f, // contenteditable
+	0xa6:  0x66908, // onsubmit
+	0xa8:  0x44f09, // oninvalid
+	0xaa:  0x202,   // br
+	0xab:  0x10902, // id
+	0xac:  0x5d04,  // loop
+	0xad:  0x5630a, // onpageshow
+	0xb0:  0x2cf04, // href
+	0xb2:  0x2210a, // figcaption
+	0xb3:  0x2690e, // onautocomplete
+	0xb4:  0x49106, // onload
+	0xb6:  0x9c04,  // rows
+	0xb7:  0x1a605, // nonce
+	0xb8:  0x68a14, // onunhandledrejection
+	0xbb:  0x21306, // center
+	0xbc:  0x59406, // onplay
+	0xbd:  0x33f02, // h5
+	0xbe:  0x49d07, // listing
+	0xbf:  0x57606, // public
+	0xc2:  0x23b06, // figure
+	0xc3:  0x57a04, // icon
+	0xc4:  0x1ab03, // col
+	0xc5:  0x47b03, // rel
+	0xc6:  0xe605,  // media
+	0xc7:  0x12109, // autofocus
+	0xc8:  0x19a02, // rt
+	0xca:  0x2d304, // lang
+	0xcc:  0x49908, // datalist
+	0xce:  0x2eb06, // iframe
+	0xcf:  0x36105, // muted
+	0xd0:  0x6140a, // onauxclick
+	0xd2:  0x3c02,  // as
+	0xd6:  0x3fd06, // ondrop
+	0xd7:  0x1c90a, // annotation
+	0xd8:  0x21908, // fieldset
+	0xdb:  0x2cf08, // hreflang
+	0xdc:  0x4e70c, // onmouseenter
+	0xdd:  0x2a402, // mn
+	0xde:  0xe60a,  // mediagroup
+	0xdf:  0x9805,  // meter
+	0xe0:  0x56c03, // wbr
+	0xe2:  0x63e05, // width
+	0xe3:  0x2290c, // onafterprint
+	0xe4:  0x30505, // ismap
+	0xe5:  0x1505,  // value
+	0xe7:  0x1303,  // nav
+	0xe8:  0x54508, // ononline
+	0xe9:  0xb604,  // mark
+	0xea:  0xc303,  // low
+	0xeb:  0x3ee0b, // ondragstart
+	0xef:  0x12f03, // xmp
+	0xf0:  0x22407, // caption
+	0xf1:  0xd904,  // type
+	0xf2:  0x70907, // summary
+	0xf3:  0x6802,  // tt
+	0xf4:  0x20809, // translate
+	0xf5:  0x1870a, // blockquote
+	0xf8:  0x15702, // hr
+	0xfa:  0x2705,  // tbody
+	0xfc:  0x7b07,  // picture
+	0xfd:  0x5206,  // height
+	0xfe:  0x19c04, // cite
+	0xff:  0x2501,  // s
+	0x101: 0xff05,  // async
+	0x102: 0x56f07, // onpaste
+	0x103: 0x19507, // onabort
+	0x104: 0x2b706, // target
+	0x105: 0x14b03, // bdo
+	0x106: 0x1f006, // coords
+	0x107: 0x5e108, // onresize
+	0x108: 0x71908, // template
+	0x10a: 0x3a02,  // rb
+	0x10b: 0x2a50a, // novalidate
+	0x10c: 0x460e,  // updateviacache
+	0x10d: 0x71003, // sup
+	0x10e: 0x6c07,  // noembed
+	0x10f: 0x16b03, // div
+	0x110: 0x6f707, // srclang
+	0x111: 0x17a09, // draggable
+	0x112: 0x67305, // scope
+	0x113: 0x5905,  // label
+	0x114: 0x22f02, // rp
+	0x115: 0x23f08, // required
+	0x116: 0x3780d, // oncontextmenu
+	0x117: 0x5e504, // size
+	0x118: 0x5b00a, // spellcheck
+	0x119: 0x3f04,  // font
+	0x11a: 0x9c07,  // rowspan
+	0x11b: 0x10a07, // default
+	0x11d: 0x44307, // oninput
+	0x11e: 0x38506, // itemid
+	0x11f: 0x5ee04, // code
+	0x120: 0xaa07,  // acronym
+	0x121: 0x3b04,  // base
+	0x125: 0x2470d, // foreignObject
+	0x126: 0x2ca04, // high
+	0x127: 0x3cb0e, // referrerpolicy
+	0x128: 0x33703, // max
+	0x129: 0x59d0a, // onpopstate
+	0x12a: 0x2fc02, // h4
+	0x12b: 0x4ac04, // meta
+	0x12c: 0x17305, // blink
+	0x12e: 0x5f508, // onscroll
+	0x12f: 0x59409, // onplaying
+	0x130: 0xc113,  // allowpaymentrequest
+	0x131: 0x19a03, // rtc
+	0x132: 0x72b04, // wrap
+	0x134: 0x8b08,  // frameset
+	0x135: 0x32605, // small
+	0x137: 0x32006, // header
+	0x138: 0x40409, // onemptied
+	0x139: 0x34902, // h6
+	0x13a: 0x35908, // multiple
+	0x13c: 0x52a06, // prompt
+	0x13f: 0x28e09, // challenge
+	0x141: 0x4370c, // onhashchange
+	0x142: 0x57b07, // content
+	0x143: 0x1c90e, // annotation-xml
+	0x144: 0x36607, // onclose
+	0x145: 0x14d10, // oncanplaythrough
+	0x148: 0x5170b, // onmouseover
+	0x149: 0x64f08, // sortable
+	0x14a: 0xa402,  // mo
+	0x14b: 0x2cd02, // h3
+	0x14c: 0x2c406, // script
+	0x14d: 0x41d07, // onended
+	0x14f: 0x64706, // poster
+	0x150: 0x7210a, // workertype
+	0x153: 0x1f505, // shape
+	0x154: 0x4,     // abbr
+	0x155: 0x1,     // a
+	0x156: 0x2bf02, // dd
+	0x157: 0x71606, // system
+	0x158: 0x4ce0e, // onmessageerror
+	0x159: 0x36b08, // seamless
+	0x15a: 0x2610a, // formaction
+	0x15b: 0x6e106, // option
+	0x15c: 0x31d04, // math
+	0x15d: 0x62609, // onseeking
+	0x15e: 0x39c05, // oncut
+	0x15f: 0x44c03, // del
+	0x160: 0x11005, // title
+	0x161: 0x11505, // audio
+	0x162: 0x63108, // selected
+	0x165: 0x3b40b, // ondragenter
+	0x166: 0x46e06, // spacer
+	0x167: 0x4a410, // onloadedmetadata
+	0x168: 0x44505, // input
+	0x16a: 0x58505, // table
+	0x16b: 0x41508, // onchange
+	0x16e: 0x5f005, // defer
+	0x171: 0x50a0a, // onmouseout
+	0x172: 0x20504, // slot
+	0x175: 0x3704,  // nobr
+	0x177: 0x1d707, // command
+	0x17a: 0x7207,  // details
+	0x17b: 0x38104, // menu
+	0x17c: 0xb903,  // kbd
+	0x17d: 0x57304, // step
+	0x17e: 0x20303, // ins
+	0x17f: 0x13c08, // autoplay
+	0x182: 0x34103, // min
+	0x183: 0x17404, // link
+	0x185: 0x40d10, // ondurationchange
+	0x186: 0x9202,  // td
+	0x187: 0x8b05,  // frame
+	0x18a: 0x2ab08, // datetime
+	0x18b: 0x44509, // inputmode
+	0x18c: 0x35108, // readonly
+	0x18d: 0x21104, // face
+	0x18f: 0x5e505, // sizes
+	0x191: 0x4b208, // tabindex
+	0x192: 0x6db06, // strong
+	0x193: 0xba03,  // bdi
+	0x194: 0x6fe06, // srcset
+	0x196: 0x67202, // ms
+	0x197: 0x5b507, // checked
+	0x198: 0xb105,  // align
+	0x199: 0x1e507, // section
+	0x19b: 0x6e05,  // embed
+	0x19d: 0x15e07, // bgsound
+	0x1a2: 0x49d04, // list
+	0x1a3: 0x61e08, // onseeked
+	0x1a4: 0x66009, // onstorage
+	0x1a5: 0x2f603, // img
+	0x1a6: 0xf505,  // tfoot
+	0x1a9: 0x26913, // onautocompleteerror
+	0x1aa: 0x5fd19, // onsecuritypolicyviolation
+	0x1ad: 0x9303,  // dir
+	0x1ae: 0x9307,  // dirname
+	0x1b0: 0x5a70a, // onprogress
+	0x1b2: 0x65709, // onstalled
+	0x1b5: 0x66f09, // itemscope
+	0x1b6: 0x49904, // data
+	0x1b7: 0x3d90b, // ondragleave
+	0x1b8: 0x56102, // h2
+	0x1b9: 0x2f706, // mglyph
+	0x1ba: 0x16502, // is
+	0x1bb: 0x6e50e, // onbeforeunload
+	0x1bc: 0x2830d, // typemustmatch
+	0x1bd: 0x3ab06, // ondrag
+	0x1be: 0x5da07, // onreset
+	0x1c0: 0x51106, // output
+	0x1c1: 0x12907, // sandbox
+	0x1c2: 0x1b209, // plaintext
+	0x1c4: 0x34c08, // textarea
+	0x1c7: 0xd607,  // keytype
+	0x1c8: 0x34b05, // mtext
+	0x1c9: 0x6b10e, // onvolumechange
+	0x1ca: 0x1ea06, // onblur
+	0x1cb: 0x58a07, // onpause
+	0x1cd: 0x5bc0c, // onratechange
+	0x1ce: 0x10705, // aside
+	0x1cf: 0x6cf07, // optimum
+	0x1d1: 0x45809, // onkeydown
+	0x1d2: 0x1c407, // colspan
+	0x1d3: 0x1004,  // main
+	0x1d4: 0x66b03, // sub
+	0x1d5: 0x25b06, // object
+	0x1d6: 0x55c06, // search
+	0x1d7: 0x37206, // sorted
+	0x1d8: 0x17003, // big
+	0x1d9: 0xb01,   // u
+	0x1db: 0x26b0c, // autocomplete
+	0x1dc: 0xcc02,  // tr
+	0x1dd: 0xf303,  // alt
+	0x1df: 0x7804,  // samp
+	0x1e0: 0x5c812, // onrejectionhandled
+	0x1e1: 0x4f30c, // onmouseleave
+	0x1e2: 0x28007, // enctype
+	0x1e3: 0xa208,  // nomodule
+	0x1e5: 0x3280f, // allowfullscreen
+	0x1e6: 0x5f08,  // optgroup
+	0x1e8: 0x27c0b, // formenctype
+	0x1e9: 0x18106, // legend
+	0x1ea: 0x10306, // canvas
+	0x1eb: 0x6607,  // pattern
+	0x1ec: 0x2c208, // noscript
+	0x1ed: 0x601,   // i
+	0x1ee: 0x5d602, // dl
+	0x1ef: 0xa702,  // ul
+	0x1f2: 0x52209, // onmouseup
+	0x1f4: 0x1ba05, // track
+	0x1f7: 0x3a10a, // ondblclick
+	0x1f8: 0x3bf0a, // ondragexit
+	0x1fa: 0x8703,  // dfn
+	0x1fc: 0x26506, // action
+	0x1fd: 0x35004, // area
+	0x1fe: 0x31607, // marquee
+	0x1ff: 0x16d03, // var
 }
 
 const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" +
@@ -758,26 +760,26 @@ const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb"
 	"dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" +
 	"bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" +
 	"penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" +
-	"ntrolshapecoordslotranslatecrossoriginsmallowfullscreenoscri" +
-	"ptfacenterfieldsetfigcaptionafterprintegrityfigurequiredfore" +
-	"ignObjectforeignobjectformactionautocompleteerrorformenctype" +
-	"mustmatchallengeformmethodformnovalidatetimeformtargethgroup" +
-	"osterhiddenhigh2hreflanghttp-equivideonclickiframeimageimgly" +
-	"ph3isindexismappletitemtypemarqueematheadersortedmaxlength4m" +
-	"inlength5mtextareadonlymultiplemutedoncloseamlessourceoncont" +
-	"extmenuitemidoncopyoncuechangeoncutondblclickondragendondrag" +
-	"enterondragexitemreferrerpolicyondragleaveondragoverondragst" +
-	"articleondropzonemptiedondurationchangeonendedonerroronfocus" +
-	"paceronhashchangeoninputmodeloninvalidonkeydownloadonkeypres" +
-	"spellcheckedonkeyupreloadonlanguagechangeonloadeddatalisting" +
-	"onloadedmetadatabindexonloadendonloadstartonmessageerroronmo" +
-	"usedownonmouseenteronmouseleaveonmousemoveonmouseoutputonmou" +
-	"seoveronmouseupromptonmousewheelonofflineononlineonpagehides" +
-	"classectionbluronpageshowbronpastepublicontenteditableonpaus" +
-	"emaponplayingonpopstateonprogressrcdocodeferonratechangeonre" +
-	"jectionhandledonresetonresizesrclangonscrollonsecuritypolicy" +
-	"violationauxclickonseekedonseekingonselectedonshowidth6onsor" +
-	"tableonstalledonstorageonsubmitemscopedonsuspendontoggleonun" +
-	"handledrejectionbeforeprintonunloadonvolumechangeonwaitingon" +
-	"wheeloptimumanifestrongoptionbeforeunloaddressrcsetstylesumm" +
-	"arysupsvgsystemplateworkertypewrap"
+	"ntrolsectionblurcoordshapecrossoriginslotranslatefacenterfie" +
+	"ldsetfigcaptionafterprintegrityfigurequiredforeignObjectfore" +
+	"ignobjectformactionautocompleteerrorformenctypemustmatchalle" +
+	"ngeformmethodformnovalidatetimeformtargethiddenoscripthigh3h" +
+	"reflanghttp-equivideonclickiframeimageimglyph4isindexismappl" +
+	"etitemtypemarqueematheadersmallowfullscreenmaxlength5minleng" +
+	"th6mtextareadonlymultiplemutedoncloseamlessortedoncontextmen" +
+	"uitemidoncopyoncuechangeoncutondblclickondragendondragentero" +
+	"ndragexitemreferrerpolicyondragleaveondragoverondragstarticl" +
+	"eondropzonemptiedondurationchangeonendedonerroronfocusourceo" +
+	"nhashchangeoninputmodeloninvalidonkeydownloadonkeypresspacer" +
+	"onkeyupreloadonlanguagechangeonloadeddatalistingonloadedmeta" +
+	"databindexonloadendonloadstartonmessageerroronmousedownonmou" +
+	"seenteronmouseleaveonmousemoveonmouseoutputonmouseoveronmous" +
+	"eupromptonmousewheelonofflineononlineonpagehidesclassearch2o" +
+	"npageshowbronpastepublicontenteditableonpausemaponplayingonp" +
+	"opstateonprogresspellcheckedonratechangeonrejectionhandledon" +
+	"resetonresizesrcdocodeferonscrollonsecuritypolicyviolationau" +
+	"xclickonseekedonseekingonselectedonshowidthgrouposteronsorta" +
+	"bleonstalledonstorageonsubmitemscopedonsuspendontoggleonunha" +
+	"ndledrejectionbeforeprintonunloadonvolumechangeonwaitingonwh" +
+	"eeloptimumanifestrongoptionbeforeunloaddressrclangsrcsetstyl" +
+	"esummarysupsvgsystemplateworkertypewrap"
diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go
index 643c674e3..518ee4c94 100644
--- a/vendor/golang.org/x/net/html/parse.go
+++ b/vendor/golang.org/x/net/html/parse.go
@@ -924,7 +924,7 @@ func inBodyIM(p *parser) bool {
 			p.addElement()
 			p.im = inFramesetIM
 			return true
-		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
+		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Search, a.Section, a.Summary, a.Ul:
 			p.popUntil(buttonScope, a.P)
 			p.addElement()
 		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
@@ -1136,7 +1136,7 @@ func inBodyIM(p *parser) bool {
 				return false
 			}
 			return true
-		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
+		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul:
 			p.popUntil(defaultScope, p.tok.DataAtom)
 		case a.Form:
 			if p.oe.contains(a.Template) {
diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go
index 3c57880d6..6598c1f7b 100644
--- a/vendor/golang.org/x/net/html/token.go
+++ b/vendor/golang.org/x/net/html/token.go
@@ -839,8 +839,22 @@ func (z *Tokenizer) readStartTag() TokenType {
 	if raw {
 		z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end]))
 	}
-	// Look for a self-closing token like "<br/>".
-	if z.err == nil && z.buf[z.raw.end-2] == '/' {
+	// Look for a self-closing token (e.g. <br/>).
+	//
+	// Originally, we did this by just checking that the last character of the
+	// tag (ignoring the closing bracket) was a solidus (/) character, but this
+	// is not always accurate.
+	//
+	// We need to be careful that we don't misinterpret a non-self-closing tag
+	// as self-closing, as can happen if the tag contains unquoted attribute
+	// values (i.e. <p a=/>).
+	//
+	// To avoid this, we check that the last non-bracket character of the tag
+	// (z.raw.end-2) isn't the same character as the last non-quote character of
+	// the last attribute of the tag (z.pendingAttr[1].end-1), if the tag has
+	// attributes.
+	nAttrs := len(z.attr)
+	if z.err == nil && z.buf[z.raw.end-2] == '/' && (nAttrs == 0 || z.raw.end-2 != z.attr[nAttrs-1][1].end-1) {
 		return SelfClosingTagToken
 	}
 	return StartTagToken
diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go
index 81faec7e7..97bd8b06f 100644
--- a/vendor/golang.org/x/net/http2/frame.go
+++ b/vendor/golang.org/x/net/http2/frame.go
@@ -225,6 +225,11 @@ var fhBytes = sync.Pool{
 	},
 }
 
+func invalidHTTP1LookingFrameHeader() FrameHeader {
+	fh, _ := readFrameHeader(make([]byte, frameHeaderLen), strings.NewReader("HTTP/1.1 "))
+	return fh
+}
+
 // ReadFrameHeader reads 9 bytes from r and returns a FrameHeader.
 // Most users should use Framer.ReadFrame instead.
 func ReadFrameHeader(r io.Reader) (FrameHeader, error) {
@@ -503,10 +508,16 @@ func (fr *Framer) ReadFrame() (Frame, error) {
 		return nil, err
 	}
 	if fh.Length > fr.maxReadSize {
+		if fh == invalidHTTP1LookingFrameHeader() {
+			return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", err)
+		}
 		return nil, ErrFrameTooLarge
 	}
 	payload := fr.getReadBuf(fh.Length)
 	if _, err := io.ReadFull(fr.r, payload); err != nil {
+		if fh == invalidHTTP1LookingFrameHeader() {
+			return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", err)
+		}
 		return nil, err
 	}
 	f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, fr.countError, payload)
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index b640deb0e..51fca38f6 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -1068,7 +1068,10 @@ func (sc *serverConn) serve(conf http2Config) {
 
 func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) {
 	if sc.pingSent {
-		sc.vlogf("timeout waiting for PING response")
+		sc.logf("timeout waiting for PING response")
+		if f := sc.countErrorFunc; f != nil {
+			f("conn_close_lost_ping")
+		}
 		sc.conn.Close()
 		return
 	}
diff --git a/vendor/golang.org/x/net/websocket/websocket.go b/vendor/golang.org/x/net/websocket/websocket.go
index ac76165ce..3448d2039 100644
--- a/vendor/golang.org/x/net/websocket/websocket.go
+++ b/vendor/golang.org/x/net/websocket/websocket.go
@@ -6,9 +6,10 @@
 // as specified in RFC 6455.
 //
 // This package currently lacks some features found in an alternative
-// and more actively maintained WebSocket package:
+// and more actively maintained WebSocket packages:
 //
-//	https://pkg.go.dev/github.com/coder/websocket
+//   - [github.com/gorilla/websocket]
+//   - [github.com/coder/websocket]
 package websocket // import "golang.org/x/net/websocket"
 
 import (
diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go
index a4ea5d14f..f8c3c0926 100644
--- a/vendor/golang.org/x/sync/errgroup/errgroup.go
+++ b/vendor/golang.org/x/sync/errgroup/errgroup.go
@@ -18,7 +18,7 @@ import (
 type token struct{}
 
 // A Group is a collection of goroutines working on subtasks that are part of
-// the same overall task.
+// the same overall task. A Group should not be reused for different tasks.
 //
 // A zero Group is valid, has no limit on the number of active goroutines,
 // and does not cancel on error.
@@ -61,6 +61,7 @@ func (g *Group) Wait() error {
 }
 
 // Go calls the given function in a new goroutine.
+// The first call to Go must happen before a Wait.
 // It blocks until the new goroutine can be added without the number of
 // active goroutines in the group exceeding the configured limit.
 //
diff --git a/vendor/modules.txt b/vendor/modules.txt
index e63f8efb9..997ecd70c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -466,7 +466,7 @@ golang.org/x/exp/slices
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/module
 golang.org/x/mod/semver
-# golang.org/x/net v0.37.0
+# golang.org/x/net v0.38.0
 ## explicit; go 1.23.0
 golang.org/x/net/context
 golang.org/x/net/html
@@ -486,7 +486,7 @@ golang.org/x/net/websocket
 ## explicit; go 1.23.0
 golang.org/x/oauth2
 golang.org/x/oauth2/internal
-# golang.org/x/sync v0.12.0
+# golang.org/x/sync v0.13.0
 ## explicit; go 1.23.0
 golang.org/x/sync/errgroup
 golang.org/x/sync/singleflight
@@ -1670,7 +1670,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/field
 sigs.k8s.io/kustomize/kyaml/yaml/merge2
 sigs.k8s.io/kustomize/kyaml/yaml/schema
 sigs.k8s.io/kustomize/kyaml/yaml/walk
-# sigs.k8s.io/lws v0.6.0
+# sigs.k8s.io/lws v0.6.1
 ## explicit; go 1.24.0
 sigs.k8s.io/lws/api/leaderworkerset/scheme
 sigs.k8s.io/lws/api/leaderworkerset/v1



Test patch:
diff --git a/pkg/cache/tas_cache_test.go b/pkg/cache/tas_cache_test.go
index 4d67498d1..aa453ac01 100644
--- a/pkg/cache/tas_cache_test.go
+++ b/pkg/cache/tas_cache_test.go
@@ -295,6 +295,7 @@ func TestFindTopologyAssignment(t *testing.T) {
 		topologyRequest    *kueue.PodSetTopologyRequest
 		levels             []string
 		nodeLabels         map[string]string
+		nodeSelector       map[string]string
 		nodes              []corev1.Node
 		pods               []corev1.Pod
 		requests           resources.Requests
@@ -1944,6 +1945,116 @@ func TestFindTopologyAssignment(t *testing.T) {
 			wantReason:         `topology "default" doesn't allow to fit any of 1 pod(s)`,
 			enableFeatureGates: []featuregate.Feature{features.TASProfileMostFreeCapacity},
 		},
+		"skip node which doesn't match node selector, missing label; BestFit": {
+			nodes: []corev1.Node{
+				*testingnode.MakeNode("x1").
+					Label("zone", "zone-a").
+					Label(corev1.LabelHostname, "x1").
+					StatusAllocatable(corev1.ResourceList{
+						corev1.ResourceCPU:    resource.MustParse("1"),
+						corev1.ResourceMemory: resource.MustParse("1Gi"),
+						corev1.ResourcePods:   resource.MustParse("10"),
+					}).
+					Ready().
+					Obj(),
+			},
+			topologyRequest: &kueue.PodSetTopologyRequest{
+				Required: ptr.To(corev1.LabelHostname),
+			},
+			nodeLabels: map[string]string{
+				"zone": "zone-a",
+			},
+			levels: defaultOneLevel,
+			requests: resources.Requests{
+				corev1.ResourceCPU: 300,
+			},
+			nodeSelector: map[string]string{
+				"custom-label-1": "custom-value-1",
+			},
+			count:      1,
+			wantReason: `topology "default" doesn't allow to fit any of 1 pod(s)`,
+		},
+		"skip node which doesn't match node selector, label exists, value doesn't match; BestFit": {
+			nodes: []corev1.Node{
+				*testingnode.MakeNode("x1").
+					Label("zone", "zone-a").
+					Label(corev1.LabelHostname, "x1").
+					Label("custom-label-1", "value-1").
+					StatusAllocatable(corev1.ResourceList{
+						corev1.ResourceCPU:    resource.MustParse("1"),
+						corev1.ResourceMemory: resource.MustParse("1Gi"),
+						corev1.ResourcePods:   resource.MustParse("10"),
+					}).
+					Ready().
+					Obj(),
+			},
+			topologyRequest: &kueue.PodSetTopologyRequest{
+				Required: ptr.To(corev1.LabelHostname),
+			},
+			nodeLabels: map[string]string{
+				"zone": "zone-a",
+			},
+			nodeSelector: map[string]string{
+				"custom-label-1": "value-2",
+			},
+			levels: defaultOneLevel,
+			requests: resources.Requests{
+				corev1.ResourceCPU: 300,
+			},
+			count:      1,
+			wantReason: `topology "default" doesn't allow to fit any of 1 pod(s)`,
+		},
+		"allow to schedule on node which matches node; BestFit": {
+			nodes: []corev1.Node{
+				*testingnode.MakeNode("b1-r1-x1").
+					Label("zone", "zone-a").
+					Label(corev1.LabelHostname, "x1").
+					Label("custom-label-1", "value-1").
+					StatusAllocatable(corev1.ResourceList{
+						corev1.ResourceCPU:    resource.MustParse("1"),
+						corev1.ResourceMemory: resource.MustParse("1Gi"),
+						corev1.ResourcePods:   resource.MustParse("10"),
+					}).
+					Ready().
+					Obj(),
+				*testingnode.MakeNode("b1-r1-x2").
+					Label("zone", "zone-a").
+					Label(corev1.LabelHostname, "x2").
+					Label("custom-label-1", "value-2").
+					StatusAllocatable(corev1.ResourceList{
+						corev1.ResourceCPU:    resource.MustParse("1"),
+						corev1.ResourceMemory: resource.MustParse("1Gi"),
+						corev1.ResourcePods:   resource.MustParse("10"),
+					}).
+					Ready().
+					Obj(),
+			},
+			topologyRequest: &kueue.PodSetTopologyRequest{
+				Required: ptr.To(corev1.LabelHostname),
+			},
+			nodeLabels: map[string]string{
+				"zone": "zone-a",
+			},
+			nodeSelector: map[string]string{
+				"custom-label-1": "value-2",
+			},
+			levels: defaultOneLevel,
+			requests: resources.Requests{
+				corev1.ResourceCPU: 1000,
+			},
+			count: 1,
+			wantAssignment: &kueue.TopologyAssignment{
+				Levels: defaultOneLevel,
+				Domains: []kueue.TopologyDomainAssignment{
+					{
+						Count: 1,
+						Values: []string{
+							"x2",
+						},
+					},
+				},
+			},
+		},
 	}
 	for name, tc := range cases {
 		t.Run(name, func(t *testing.T) {
@@ -1978,7 +2089,8 @@ func TestFindTopologyAssignment(t *testing.T) {
 					TopologyRequest: tc.topologyRequest,
 					Template: corev1.PodTemplateSpec{
 						Spec: corev1.PodSpec{
-							Tolerations: tc.tolerations,
+							Tolerations:  tc.tolerations,
+							NodeSelector: tc.nodeSelector,
 						},
 					},
 				},
diff --git a/pkg/controller/admissionchecks/provisioning/controller_test.go b/pkg/controller/admissionchecks/provisioning/controller_test.go
index 03b053f17..3af8181de 100644
--- a/pkg/controller/admissionchecks/provisioning/controller_test.go
+++ b/pkg/controller/admissionchecks/provisioning/controller_test.go
@@ -516,36 +516,6 @@ func TestReconcile(t *testing.T) {
 			templates:            []corev1.PodTemplate{*baseTemplate1.DeepCopy(), *baseTemplate2.DeepCopy()},
 			wantRequestsNotFound: []string{"wl-check1"},
 		},
-		"KeepQuotaForProvReqRetry; when request fails and is retried": {
-			workload: baseWorkload.DeepCopy(),
-			checks:   []kueue.AdmissionCheck{*baseCheck.DeepCopy()},
-			flavors:  []kueue.ResourceFlavor{*baseFlavor1.DeepCopy(), *baseFlavor2.DeepCopy()},
-			configs:  []kueue.ProvisioningRequestConfig{*baseConfigWithRetryStrategy.Clone().RetryLimit(2).BaseBackoff(0).Obj()},
-			requests: []autoscaling.ProvisioningRequest{
-				*requestWithCondition(baseRequest, autoscaling.Failed, metav1.ConditionTrue),
-			},
-			enableGates: []featuregate.Feature{features.KeepQuotaForProvReqRetry},
-			templates:   []corev1.PodTemplate{*baseTemplate1.DeepCopy(), *baseTemplate2.DeepCopy()},
-			wantWorkloads: map[string]*kueue.Workload{
-				baseWorkload.GetName(): (&utiltesting.WorkloadWrapper{Workload: *baseWorkload.DeepCopy()}).
-					AdmissionChecks(kueue.AdmissionCheckState{
-						Name:    "check1",
-						State:   kueue.CheckStatePending,
-						Message: "Retrying after failure: ",
-					}, kueue.AdmissionCheckState{
-						Name:  "not-provisioning",
-						State: kueue.CheckStatePending,
-					}).
-					Obj(),
-			},
-			wantEvents: []utiltesting.EventRecord{
-				{
-					Key:       client.ObjectKeyFromObject(baseWorkload),
-					EventType: corev1.EventTypeNormal,
-					Reason:    "ProvisioningRequestCreated",
-					Message:   `Created ProvisioningRequest: "wl-check1-2"`,
-				},
-			}},
 		"when request fails and is retried": {
 			workload: baseWorkload.DeepCopy(),
 			checks:   []kueue.AdmissionCheck{*baseCheck.DeepCopy()},
@@ -971,92 +941,6 @@ func TestReconcile(t *testing.T) {
 					Obj(),
 			},
 		},
-		"KeepQuotaForProvReqRetry; workload retries the admission check when is not admitted and receives the provisioning request's BookingExpired condition": {
-			workload: (&utiltesting.WorkloadWrapper{Workload: *baseWorkload.DeepCopy()}).
-				Admitted(false).
-				Obj(),
-			checks:      []kueue.AdmissionCheck{*baseCheck.DeepCopy()},
-			flavors:     []kueue.ResourceFlavor{*baseFlavor1.DeepCopy(), *baseFlavor2.DeepCopy()},
-			configs:     []kueue.ProvisioningRequestConfig{*baseConfigWithRetryStrategy.Clone().RetryLimit(1).BaseBackoff(0).Obj()},
-			enableGates: []featuregate.Feature{features.KeepQuotaForProvReqRetry},
-			requests: []autoscaling.ProvisioningRequest{
-				*requestWithConditions(baseRequest,
-					[]metav1.Condition{
-						{
-							Type:   autoscaling.Failed,
-							Status: metav1.ConditionFalse,
-						},
-						{
-							Type:   autoscaling.Provisioned,
-							Status: metav1.ConditionTrue,
-						},
-						{
-							Type:   autoscaling.Accepted,
-							Status: metav1.ConditionTrue,
-						},
-						{
-							Type:   autoscaling.BookingExpired,
-							Status: metav1.ConditionTrue,
-						},
-					}),
-			},
-			wantWorkloads: map[string]*kueue.Workload{
-				baseWorkload.GetName(): (&utiltesting.WorkloadWrapper{Workload: *baseWorkload.DeepCopy()}).
-					AdmissionChecks(kueue.AdmissionCheckState{
-						Name:    "check1",
-						State:   kueue.CheckStatePending,
-						Message: "Retrying after booking expired: ",
-					}, kueue.AdmissionCheckState{
-						Name:  "not-provisioning",
-						State: kueue.CheckStatePending,
-					}).
-					Admitted(false).
-					Obj(),
-			},
-			wantRequests: map[string]*autoscaling.ProvisioningRequest{
-				ProvisioningRequestName("wl", kueue.AdmissionCheckReference(baseCheck.Name), 2): {
-					ObjectMeta: metav1.ObjectMeta{
-						Namespace: TestNamespace,
-						Name:      "wl-check1-2",
-						Labels: map[string]string{
-							constants.ManagedByKueueLabelKey: constants.ManagedByKueueLabelValue,
-						},
-						OwnerReferences: []metav1.OwnerReference{
-							{
-								Name: "wl",
-							},
-						},
-					},
-					Spec: autoscaling.ProvisioningRequestSpec{
-						PodSets: []autoscaling.PodSet{
-							{
-								PodTemplateRef: autoscaling.Reference{
-									Name: "ppt-wl-check1-2-ps1",
-								},
-								Count: 4,
-							},
-							{
-								PodTemplateRef: autoscaling.Reference{
-									Name: "ppt-wl-check1-2-ps2",
-								},
-								Count: 3,
-							},
-						},
-						ProvisioningClassName: "class1",
-						Parameters: map[string]autoscaling.Parameter{
-							"p1": "v1",
-						},
-					},
-				},
-			},
-			wantEvents: []utiltesting.EventRecord{
-				{
-					Key:       types.NamespacedName{Namespace: "ns", Name: "wl"},
-					EventType: corev1.EventTypeNormal,
-					Reason:    "ProvisioningRequestCreated",
-					Message:   `Created ProvisioningRequest: "wl-check1-2"`,
-				},
-			}},
 		"workload retries the admission check when is not admitted and receives the provisioning request's BookingExpired condition": {
 			workload: (&utiltesting.WorkloadWrapper{Workload: *baseWorkload.DeepCopy()}).
 				Admitted(false).
diff --git a/pkg/scheduler/flavorassigner/flavorassigner_test.go b/pkg/scheduler/flavorassigner/flavorassigner_test.go
index fc333b18f..638cf3242 100644
--- a/pkg/scheduler/flavorassigner/flavorassigner_test.go
+++ b/pkg/scheduler/flavorassigner/flavorassigner_test.go
@@ -26,6 +26,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 
+	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1alpha1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/cache"
 	"sigs.k8s.io/kueue/pkg/features"
@@ -410,7 +411,7 @@ func TestAssignFlavors(t *testing.T) {
 					},
 					Count: 1,
 				}},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "two", Resource: corev1.ResourceCPU}:    3_000,
 					{Flavor: "two", Resource: corev1.ResourceMemory}: 10 * utiltesting.Mi,
@@ -805,7 +806,7 @@ func TestAssignFlavors(t *testing.T) {
 						Count: 1,
 					},
 				},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "default", Resource: corev1.ResourceCPU}:    10_000,
 					{Flavor: "default", Resource: corev1.ResourceMemory}: 5 * utiltesting.Gi,
@@ -892,7 +893,7 @@ func TestAssignFlavors(t *testing.T) {
 					},
 					Count: 1,
 				}},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "one", Resource: corev1.ResourceCPU}: 2_000,
 				}},
@@ -974,7 +975,7 @@ func TestAssignFlavors(t *testing.T) {
 					},
 					Count: 1,
 				}},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "one", Resource: corev1.ResourceCPU}: 2_000,
 				}},
@@ -1340,7 +1341,7 @@ func TestAssignFlavors(t *testing.T) {
 				Obj(),
 			wantRepMode: Fit,
 			wantAssignment: Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 				PodSets: []PodSetAssignment{{
 					Name: kueue.DefaultPodSetName,
 					Flavors: ResourceAssignment{
@@ -1441,7 +1442,7 @@ func TestAssignFlavors(t *testing.T) {
 				Obj(),
 			wantRepMode: Fit,
 			wantAssignment: Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 				PodSets: []PodSetAssignment{{
 					Name: kueue.DefaultPodSetName,
 					Flavors: ResourceAssignment{
@@ -1498,7 +1499,7 @@ func TestAssignFlavors(t *testing.T) {
 			},
 			wantRepMode: Preempt,
 			wantAssignment: Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 				PodSets: []PodSetAssignment{{
 					Name: kueue.DefaultPodSetName,
 					Flavors: ResourceAssignment{
@@ -1555,7 +1556,7 @@ func TestAssignFlavors(t *testing.T) {
 			},
 			wantRepMode: Preempt,
 			wantAssignment: Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 				PodSets: []PodSetAssignment{{
 					Name: kueue.DefaultPodSetName,
 					Flavors: ResourceAssignment{
@@ -1768,7 +1769,7 @@ func TestAssignFlavors(t *testing.T) {
 					},
 					Count: 1,
 				}},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "one", Resource: corev1.ResourceCPU}:  9_000,
 					{Flavor: "one", Resource: corev1.ResourcePods}: 1,
@@ -1818,7 +1819,7 @@ func TestAssignFlavors(t *testing.T) {
 					},
 					Count: 1,
 				}},
-				Borrowing: true,
+				Borrowing: 1,
 				Usage: workload.Usage{Quota: resources.FlavorResourceQuantities{
 					{Flavor: "one", Resource: corev1.ResourceCPU}:  9_000,
 					{Flavor: "one", Resource: corev1.ResourcePods}: 1,
@@ -1856,7 +1857,7 @@ func TestAssignFlavors(t *testing.T) {
 			},
 			wantRepMode: Preempt,
 			wantAssignment: Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 				PodSets: []PodSetAssignment{{
 					Name: kueue.DefaultPodSetName,
 					Flavors: ResourceAssignment{
@@ -2322,3 +2323,135 @@ func TestLastAssignmentOutdated(t *testing.T) {
 		})
 	}
 }
+
+// We have 3 flavors: one, two, three and a 3-level cohort hierarchy where
+// each cohort get 4 units of CPU in a different flavor.
+func TestHierarchical(t *testing.T) {
+	type rfMap = map[corev1.ResourceName]kueue.ResourceFlavorReference
+	cases := map[string]struct {
+		workloadRequests       *utiltesting.PodSetWrapper
+		testClusterQueueUsage  resources.FlavorResourceQuantities
+		otherClusterQueueUsage resources.FlavorResourceQuantities
+		flavorFungibility      *kueue.FlavorFungibility
+		wantMode               FlavorAssignmentMode
+		wantAssigment          rfMap
+	}{
+		"Select the top flavor": {
+			workloadRequests: utiltesting.MakePodSet(kueue.DefaultPodSetName, 1).Request(corev1.ResourceCPU, "4"),
+			testClusterQueueUsage: resources.FlavorResourceQuantities{
+				{Flavor: "one", Resource: corev1.ResourceCPU}: 4_000,
+			},
+			otherClusterQueueUsage: resources.FlavorResourceQuantities{
+				{Flavor: "two", Resource: corev1.ResourceCPU}: 4_000,
+			},
+			wantMode:      Fit,
+			wantAssigment: rfMap{corev1.ResourceCPU: "three"},
+		},
+		"Select the first flavor which fits": {
+			workloadRequests: utiltesting.MakePodSet(kueue.DefaultPodSetName, 1).Request(corev1.ResourceCPU, "4"),
+			testClusterQueueUsage: resources.FlavorResourceQuantities{
+				{Flavor: "one", Resource: corev1.ResourceCPU}: 4_000,
+			},
+			wantMode:      Fit,
+			wantAssigment: rfMap{corev1.ResourceCPU: "two"},
+		},
+	}
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			ctx, _ := utiltesting.ContextWithLog(t)
+			resourceFlavors := map[kueue.ResourceFlavorReference]*kueue.ResourceFlavor{
+				"one":   utiltesting.MakeResourceFlavor("one").Obj(),
+				"two":   utiltesting.MakeResourceFlavor("two").Obj(),
+				"three": utiltesting.MakeResourceFlavor("three").Obj(),
+			}
+			cohorts := []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("three").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("three").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("two").
+					Parent("three").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("two").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("one").
+					Parent("two").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("one").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+			}
+			testCq := utiltesting.MakeClusterQueue("test-clusterqueue").
+				Cohort("one").
+				Preemption(kueue.ClusterQueuePreemption{
+					WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+					ReclaimWithinCohort: kueue.PreemptionPolicyLowerPriority,
+				}).
+				ResourceGroup(
+					utiltesting.MakeFlavorQuotas("one").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+					utiltesting.MakeFlavorQuotas("two").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+					utiltesting.MakeFlavorQuotas("three").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+				).
+				FlavorFungibility(kueue.FlavorFungibility{
+					WhenCanPreempt: kueue.TryNextFlavor,
+				}).ClusterQueue
+			otherCq := utiltesting.MakeClusterQueue("other-clusterqueue").
+				Cohort("two").ResourceGroup(
+				utiltesting.MakeFlavorQuotas("one").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+				utiltesting.MakeFlavorQuotas("two").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+				utiltesting.MakeFlavorQuotas("three").Resource(corev1.ResourceCPU, "0").FlavorQuotas,
+			).ClusterQueue
+
+			wlInfo := workload.NewInfo(&kueue.Workload{
+				Spec: kueue.WorkloadSpec{
+					PodSets: []kueue.PodSet{
+						tc.workloadRequests.PodSet,
+					},
+				},
+			})
+
+			if tc.flavorFungibility != nil {
+				testCq.Spec.FlavorFungibility = tc.flavorFungibility
+			}
+			cache := cache.New(utiltesting.NewFakeClient())
+			for _, cohort := range cohorts {
+				if err := cache.AddOrUpdateCohort(cohort); err != nil {
+					t.Fatalf("Couldn't add Cohort to cache: %v", err)
+				}
+			}
+			if err := cache.AddClusterQueue(ctx, &testCq); err != nil {
+				t.Fatalf("Failed to add CQ to cache")
+			}
+			if err := cache.AddClusterQueue(ctx, &otherCq); err != nil {
+				t.Fatalf("Failed to add CQ to cache")
+			}
+			for _, rf := range resourceFlavors {
+				cache.AddOrUpdateResourceFlavor(rf)
+			}
+
+			snapshot, err := cache.Snapshot(ctx)
+			if err != nil {
+				t.Fatalf("unexpected error while building snapshot: %v", err)
+			}
+			otherClusterQueue := snapshot.ClusterQueue("other-clusterqueue")
+			otherClusterQueue.AddUsage(workload.Usage{Quota: tc.otherClusterQueueUsage})
+
+			testClusterQueue := snapshot.ClusterQueue("test-clusterqueue")
+			testClusterQueue.AddUsage(workload.Usage{Quota: tc.testClusterQueueUsage})
+
+			flvAssigner := New(wlInfo, testClusterQueue, resourceFlavors, false, &testOracle{})
+			log := testr.NewWithOptions(t, testr.Options{Verbosity: 2})
+			assignment := flvAssigner.Assign(log, nil)
+			if gotRepMode := assignment.RepresentativeMode(); gotRepMode != tc.wantMode {
+				t.Errorf("Unexpected RepresentativeMode. got %s, want %s", gotRepMode, tc.wantMode)
+			}
+			if len(assignment.PodSets[0].Flavors) != len(tc.wantAssigment) {
+				t.Errorf("Wrong number of flavors. got %d, want %d", len(assignment.PodSets[0].Flavors), len(tc.wantAssigment))
+			}
+			for resourceName, wantFlavor := range tc.wantAssigment {
+				if gotFlavor := assignment.PodSets[0].Flavors[resourceName].Name; gotFlavor != wantFlavor {
+					t.Errorf("Unexpected flavor. got %s, want %s", gotFlavor, wantFlavor)
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/scheduler/preemption/preemption_test.go b/pkg/scheduler/preemption/preemption_test.go
index 119f81ab1..06dab3a74 100644
--- a/pkg/scheduler/preemption/preemption_test.go
+++ b/pkg/scheduler/preemption/preemption_test.go
@@ -2729,7 +2729,6 @@ func TestFairPreemptions(t *testing.T) {
 func targetKeyReason(key, reason string) string {
 	return fmt.Sprintf("%s:%s", key, reason)
 }
-
 func TestCandidatesOrdering(t *testing.T) {
 	now := time.Now()
 	candidates := []*workload.Info{
@@ -2764,7 +2763,7 @@ func TestCandidatesOrdering(t *testing.T) {
 			ReserveQuotaAt(utiltesting.MakeAdmission("self").Obj(), now.Add(time.Second)).
 			Obj()),
 	}
-	sort.Slice(candidates, candidatesOrdering(candidates, "self", now))
+	sort.Slice(candidates, CandidatesOrdering(candidates, "self", now))
 	gotNames := make([]string, len(candidates))
 	for i, c := range candidates {
 		gotNames[i] = workload.Key(c.Obj)
@@ -2816,3 +2815,1207 @@ func TestPreemptionMessage(t *testing.T) {
 		}
 	}
 }
+
+func TestHierarchicalPreemptions(t *testing.T) {
+	now := time.Now()
+	flavors := []*kueue.ResourceFlavor{
+		utiltesting.MakeResourceFlavor("default").Obj(),
+	}
+	cases := map[string]struct {
+		clusterQueues []*kueue.ClusterQueue
+		cohorts       []*kueuealpha.Cohort
+		admitted      []kueue.Workload
+		incoming      *kueue.Workload
+		targetCQ      kueue.ClusterQueueReference
+		assignment    flavorassigner.Assignment
+		wantPreempted sets.Set[string]
+	}{
+		//
+		//            R
+		//      /      |
+		//   C(2) q_borrowing(0)
+		//  /
+		// q
+		"preempt with hierarchical advantage": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted2", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(targetKeyReason("/admitted2", kueue.InCohortReclamationReason)),
+		},
+		//
+		//             R
+		//      /      |         \
+		//   C(2) q_borrowing(0)  q_nominal(2)
+		//  /
+		// q
+		"avoid queues within nominal quota": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q_nominal").
+					Cohort("r").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Resource(corev1.ResourceCPU, "2").
+					Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted1", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_nominal").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted2", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(targetKeyReason("/admitted2", kueue.InCohortReclamationReason)),
+		},
+		//
+		//            R(0)
+		//      /      |
+		//   C(2) q_borrowing(0)
+		//  /
+		// q(0)
+		"preempt multiple with hierarchical advantage": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted1", "").
+					Priority(1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted2", "").
+					Priority(2).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted1", kueue.InCohortReclamationReason),
+				targetKeyReason("/admitted2", kueue.InCohortReclamationReason)),
+		},
+		//
+		//        R(0)
+		//      /
+		//   C(3)
+		//  /   \
+		// q(0) q_same_cohort(0)
+		"preempt in cohort and own CQ": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "3").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						BorrowWithinCohort: &kueue.BorrowWithinCohort{
+							Policy:               kueue.BorrowWithinCohortPolicyLowerPriority,
+							MaxPriorityThreshold: ptr.To[int32](0),
+						},
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_not_preemptible", "").
+					Priority(1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_preemptible", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_own_queue", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(1).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_preemptible", kueue.InCohortReclaimWhileBorrowingReason),
+				targetKeyReason("/admitted_own_queue", kueue.InClusterQueueReason)),
+		},
+		//
+		//            R(0)
+		//      /      |
+		//   C(2) q_borrowing(0)
+		//  /
+		// q(0)
+		"prefer to preempt hierarchical candidate": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_queue", "").
+					Priority(-2).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "1").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason)),
+		},
+		//
+		//           R(0)
+		//      /      |
+		//   C(2)   q_nominal(2)
+		//  /   \
+		// q(0) q_same_cohort(0)
+		"forced to preempt priority candidate": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						BorrowWithinCohort: &kueue.BorrowWithinCohort{
+							Policy:               kueue.BorrowWithinCohortPolicyLowerPriority,
+							MaxPriorityThreshold: ptr.To[int32](0),
+						},
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_nominal").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_nominal", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_nominal").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_cohort", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_same_cohort", kueue.InCohortReclaimWhileBorrowingReason)),
+		},
+		//
+		//           R(0)
+		//      /      |
+		//   C(2)   q_borrowing(0)
+		//  /    \
+		// q(4)  q_same_cohort(0)
+		//
+		"incoming workload fits in CQ nominal quota": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "4").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(10).
+					Request(corev1.ResourceCPU, "3").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "3").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_cohort", "").
+					Priority(10).
+					Request(corev1.ResourceCPU, "3").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "3").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "4").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason),
+				targetKeyReason("/admitted_same_cohort", kueue.InCohortReclamationReason)),
+		},
+		//
+		//           R(1)
+		//      /      |
+		//   C(4)   q_borrowing(0)
+		//  /    \
+		// q(0)  q_same_cohort(0)
+		//
+		"preempt hierarchical and priority candidates": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Resource(corev1.ResourceCPU, "1").
+					Obj()).Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyLowerPriority,
+						BorrowWithinCohort: &kueue.BorrowWithinCohort{
+							Policy:               kueue.BorrowWithinCohortPolicyLowerPriority,
+							MaxPriorityThreshold: ptr.To[int32](0),
+						},
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_cohort_preemptible", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_not_preemptible", "").
+					Priority(1).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "3").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason),
+				targetKeyReason("/admitted_same_cohort_preemptible", kueue.InCohortReclaimWhileBorrowingReason)),
+		},
+		//
+		//           R(1)
+		//      /      |
+		//   C(4)   q_borrowing(0)
+		//  /    \
+		// q(0)  q_same_cohort(0)
+		//
+		"preempt hierarchical candidates and inside CQ": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Resource(corev1.ResourceCPU, "1").
+					Obj()).Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyLowerPriority,
+						BorrowWithinCohort: &kueue.BorrowWithinCohort{
+							Policy:               kueue.BorrowWithinCohortPolicyLowerPriority,
+							MaxPriorityThreshold: ptr.To[int32](0),
+						},
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_queue_preemptible", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_not_preemptible", "").
+					Priority(1).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "3").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason),
+				targetKeyReason("/admitted_same_queue_preemptible", kueue.InClusterQueueReason)),
+		},
+		//
+		//            R(0)
+		//      /      |          \
+		//   C(3) q_borrowing(0)  q_nominal(2)
+		//  /
+		// q(0)
+		"reclaim nominal quota from lowest priority workload, excluding non-borrowing": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "3").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_nominal").
+					Cohort("r").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Resource(corev1.ResourceCPU, "2").
+					Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing_prio_8", "").
+					Priority(8).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_prio_9", "").
+					Priority(9).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_prio_10", "").
+					Priority(9).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_nominal", "").
+					Priority(-2).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_nominal").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "1").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing_prio_8", kueue.InCohortReclamationReason)),
+		},
+		//
+		//                  R
+		//            /            \
+		//      C(2)                   C_other(2)
+		//     /    \                     |
+		//    q(0)  q_same_cohort(0)   q_other(0)
+		"infeasible preemption all available workloads in pruned subtrees": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c_other").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q_other").
+					Cohort("c_other").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_other_1", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_other").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_other_2", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_other").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_cohort", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "2").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New[string](),
+		},
+		//
+		//          R(3CPU, 0Gi)
+		//      /      |
+		//   C(4CPU,4Gi) q_borrowing(0)
+		//  /    \
+		// q(0)   q_same_cohort(0)
+		"hiearchical preemption with multiple resources": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "3").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Resource(corev1.ResourceMemory, "4Gi").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+					Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "3").
+					Request(corev1.ResourceMemory, "1Gi").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "3").
+						Assignment(corev1.ResourceMemory, "default", "1Gi").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_same_cohort", "").
+					Priority(-2).
+					Request(corev1.ResourceCPU, "1").
+					Request(corev1.ResourceMemory, "3Gi").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "1").
+						Assignment(corev1.ResourceMemory, "default", "3Gi").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(-2).
+				Request(corev1.ResourceCPU, "2").
+				Request(corev1.ResourceMemory, "1Gi").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+				corev1.ResourceMemory: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason)),
+		},
+		//
+		//           R(0)
+		//      /      |
+		//   C(2)   q_borrowing(0)
+		//  /    \
+		// q(0)  q_same_cohort(0)
+		//
+		"prefer to preempt evicted workloads": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+						BorrowWithinCohort: &kueue.BorrowWithinCohort{
+							Policy:               kueue.BorrowWithinCohortPolicyLowerPriority,
+							MaxPriorityThreshold: ptr.To[int32](0),
+						},
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_same_cohort").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("evicted_same_cohort", "").
+					Priority(-1).
+					Request(corev1.ResourceCPU, "1").
+					ReserveQuota(utiltesting.MakeAdmission("q_same_cohort").
+						Assignment(corev1.ResourceCPU, "default", "1").Obj()).
+					SetOrReplaceCondition(metav1.Condition{
+						Type:               kueue.WorkloadEvicted,
+						Status:             metav1.ConditionTrue,
+						LastTransitionTime: metav1.NewTime(now),
+					}).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(0).
+				Request(corev1.ResourceCPU, "1").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New[string](),
+		},
+		//
+		//           R(0)
+		//      /      |
+		//   C(2)   q_borrowing(0)
+		//  /
+		// q(3, lending limit 2)
+		//
+		"respect lending limits": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "2").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q").
+					Cohort("c").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "3", "", "2").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+				utiltesting.MakeClusterQueue("q_borrowing").
+					Cohort("r").ResourceGroup(
+					*utiltesting.MakeFlavorQuotas("default").Obj()).
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing", "").
+					Priority(0).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q_borrowing").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(-2).
+				Request(corev1.ResourceCPU, "5").
+				Obj(),
+			targetCQ: "q",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing", kueue.InCohortReclamationReason)),
+		},
+		//                                r
+		//                             /      \
+		//                          c11        c12
+		//                       /   |   \       \
+		//                    c21   c22    c23    q1
+		//                  /  |     |     |
+		//                c31  c32   q3    q2
+		//              /      |
+		//            q5       q4
+		//	quotas:
+		//	4: c11, c12, c21, c22, c23, c32, c31
+		//	0: q1, q3, q4, q5
+		"reclaim in complex hierarchy": {
+			cohorts: []*kueuealpha.Cohort{
+				utiltesting.MakeCohort("r").Obj(),
+				utiltesting.MakeCohort("c11").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c12").
+					Parent("r").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c21").
+					Parent("c11").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c22").
+					Parent("c11").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c23").
+					Parent("c11").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c31").
+					Parent("c21").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+				utiltesting.MakeCohort("c32").
+					Parent("c21").
+					ResourceGroup(*utiltesting.MakeFlavorQuotas("default").
+						Resource(corev1.ResourceCPU, "4").
+						Obj()).Obj(),
+			},
+			clusterQueues: []*kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("q1").
+					Cohort("c12").
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).
+					Obj(),
+				utiltesting.MakeClusterQueue("q2").
+					Cohort("c23").
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).
+					Obj(),
+				utiltesting.MakeClusterQueue("q3").
+					Cohort("c22").
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).
+					Obj(),
+				utiltesting.MakeClusterQueue("q4").
+					Cohort("c32").
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).
+					Obj(),
+				utiltesting.MakeClusterQueue("q5").
+					Cohort("c31").
+					Preemption(kueue.ClusterQueuePreemption{
+						WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+						ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+					}).
+					Obj(),
+			},
+			admitted: []kueue.Workload{
+				*utiltesting.MakeWorkload("admitted_borrowing_1", "").
+					Priority(-6).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q1").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_2", "").
+					Priority(-5).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q1").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_3", "").
+					Priority(-9).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q2").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_4", "").
+					Priority(-10).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q2").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_5", "").
+					Priority(-4).
+					Request(corev1.ResourceCPU, "4").
+					ReserveQuota(utiltesting.MakeAdmission("q3").
+						Assignment(corev1.ResourceCPU, "default", "4").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_6", "").
+					Priority(-3).
+					Request(corev1.ResourceCPU, "3").
+					ReserveQuota(utiltesting.MakeAdmission("q3").
+						Assignment(corev1.ResourceCPU, "default", "3").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_7", "").
+					Priority(4).
+					Request(corev1.ResourceCPU, "2").
+					ReserveQuota(utiltesting.MakeAdmission("q4").
+						Assignment(corev1.ResourceCPU, "default", "2").Obj()).
+					Obj(),
+				*utiltesting.MakeWorkload("admitted_borrowing_8", "").
+					Priority(2).
+					Request(corev1.ResourceCPU, "3").
+					ReserveQuota(utiltesting.MakeAdmission("q4").
+						Assignment(corev1.ResourceCPU, "default", "3").Obj()).
+					Obj(),
+			},
+			incoming: utiltesting.MakeWorkload("incoming", "").
+				Priority(-2).
+				Request(corev1.ResourceCPU, "7").
+				Obj(),
+			targetCQ: "q5",
+			assignment: singlePodSetAssignment(flavorassigner.ResourceAssignment{
+				corev1.ResourceCPU: &flavorassigner.FlavorAssignment{
+					Name: "default",
+					Mode: flavorassigner.Preempt,
+				},
+			}),
+			// only one of workloads from q2 will be preempted because
+			// after preempting the first one, the usage of cohort
+			// c23 will be back within nominal quota
+			wantPreempted: sets.New(
+				targetKeyReason("/admitted_borrowing_1", kueue.InCohortReclamationReason),
+				targetKeyReason("/admitted_borrowing_4", kueue.InCohortReclamationReason)),
+		},
+	}
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			ctx, log := utiltesting.ContextWithLog(t)
+			cl := utiltesting.NewClientBuilder().
+				WithLists(&kueue.WorkloadList{Items: tc.admitted}).
+				Build()
+
+			cqCache := cache.New(cl)
+			for _, flv := range flavors {
+				cqCache.AddOrUpdateResourceFlavor(flv)
+			}
+			for _, cq := range tc.clusterQueues {
+				if err := cqCache.AddClusterQueue(ctx, cq); err != nil {
+					t.Fatalf("Couldn't add ClusterQueue to cache: %v", err)
+				}
+			}
+			for _, cohort := range tc.cohorts {
+				if err := cqCache.AddOrUpdateCohort(cohort); err != nil {
+					t.Fatalf("Couldn't add Cohort to cache: %v", err)
+				}
+			}
+
+			var lock sync.Mutex
+			gotPreempted := sets.New[string]()
+			broadcaster := record.NewBroadcaster()
+			scheme := runtime.NewScheme()
+			if err := kueue.AddToScheme(scheme); err != nil {
+				t.Fatalf("Failed adding kueue scheme: %v", err)
+			}
+			recorder := broadcaster.NewRecorder(scheme, corev1.EventSource{Component: constants.AdmissionName})
+			preemptor := New(cl, workload.Ordering{}, recorder, config.FairSharing{}, clocktesting.NewFakeClock(now))
+			preemptor.applyPreemption = func(ctx context.Context, w *kueue.Workload, reason, _ string) error {
+				lock.Lock()
+				gotPreempted.Insert(targetKeyReason(workload.Key(w), reason))
+				lock.Unlock()
+				return nil
+			}
+			beforeSnapshot, err := cqCache.Snapshot(ctx)
+			if err != nil {
+				t.Fatalf("unexpected error while building snapshot: %v", err)
+			}
+			// make a working copy of the snapshotWorkingCopy than preemption can temporarily modify
+			snapshotWorkingCopy, err := cqCache.Snapshot(ctx)
+			if err != nil {
+				t.Fatalf("unexpected error while building snapshot: %v", err)
+			}
+			wlInfo := workload.NewInfo(tc.incoming)
+			wlInfo.ClusterQueue = tc.targetCQ
+			targets := preemptor.GetTargets(log, *wlInfo, tc.assignment, snapshotWorkingCopy)
+			_, err = preemptor.IssuePreemptions(ctx, wlInfo, targets)
+			if err != nil {
+				t.Fatalf("Failed doing preemption")
+			}
+			if diff := cmp.Diff(tc.wantPreempted, gotPreempted, cmpopts.EquateEmpty()); diff != "" {
+				t.Errorf("Issued preemptions (-want,+got):\n%s", diff)
+			}
+			if diff := cmp.Diff(beforeSnapshot, snapshotWorkingCopy, snapCmpOpts); diff != "" {
+				t.Errorf("Snapshot was modified (-initial,+end):\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go
index be9486c34..e1114d496 100644
--- a/pkg/scheduler/scheduler_test.go
+++ b/pkg/scheduler/scheduler_test.go
@@ -3043,6 +3043,69 @@ func TestSchedule(t *testing.T) {
 				"eng-alpha/a1-admitted": *utiltesting.MakeAdmission("ClusterQueueA").Assignment("gpu", "on-demand", "1").Obj(),
 			},
 		},
+		//
+		// Denote Cohorts in UPPERCASE, and ClusterQueues in lowercase.
+		// quota is at GUARANTEED cohort
+		//
+		//             ROOT
+		//          /        \
+		//    GUARANTEED      best-effort
+		//   /
+		// guaranteed
+		//
+		"in a hierarchical cohort, workload borrowing less is scheduled first": {
+			cohorts: []kueuealpha.Cohort{
+				utiltesting.MakeCohort("root").Cohort,
+				utiltesting.MakeCohort("guaranteed").
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("default").
+							Resource(corev1.ResourceCPU, "4").Obj(),
+					).Parent("root").Cohort,
+			},
+			additionalClusterQueues: []kueue.ClusterQueue{
+				utiltesting.MakeClusterQueue("guaranteed").
+					Cohort("guaranteed").
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("default").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).
+					ClusterQueue,
+				utiltesting.MakeClusterQueue("best-effort").
+					Cohort("root").
+					ResourceGroup(
+						*utiltesting.MakeFlavorQuotas("default").
+							Resource(corev1.ResourceCPU, "0").Obj(),
+					).
+					ClusterQueue,
+			},
+			additionalLocalQueues: []kueue.LocalQueue{
+				*utiltesting.MakeLocalQueue("lq-guaranteed", "eng-alpha").ClusterQueue("guaranteed").Obj(),
+				*utiltesting.MakeLocalQueue("lq-best-effort", "eng-alpha").ClusterQueue("best-effort").Obj(),
+			},
+			workloads: []kueue.Workload{
+				utiltesting.MakeWorkload("guaranteed", "eng-alpha").
+					Queue("lq-guaranteed").
+					Priority(0).
+					PodSets(utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						PodSet).
+					Workload,
+				utiltesting.MakeWorkload("best-effort", "eng-alpha").
+					Queue("lq-best-effort").
+					Priority(3).
+					PodSets(utiltesting.MakePodSet("one", 1).
+						Request(corev1.ResourceCPU, "4").
+						PodSet).
+					Workload,
+			},
+			wantAssignments: map[string]kueue.Admission{
+				"eng-alpha/guaranteed": *utiltesting.MakeAdmission("guaranteed", "one").Assignment(corev1.ResourceCPU, "default", "4").Obj(),
+			},
+			wantScheduled: []string{"eng-alpha/guaranteed"},
+			wantLeft: map[kueue.ClusterQueueReference][]string{
+				"best-effort": {"eng-alpha/best-effort"},
+			},
+		},
 	}
 
 	for name, tc := range cases {
@@ -3219,7 +3282,7 @@ func TestEntryOrdering(t *testing.T) {
 				}},
 			},
 			assignment: flavorassigner.Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 			},
 		},
 		{
@@ -3248,7 +3311,7 @@ func TestEntryOrdering(t *testing.T) {
 				}},
 			},
 			assignment: flavorassigner.Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 			},
 		},
 		{
@@ -3269,7 +3332,7 @@ func TestEntryOrdering(t *testing.T) {
 				}},
 			},
 			assignment: flavorassigner.Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 			},
 		},
 		{
@@ -3292,7 +3355,7 @@ func TestEntryOrdering(t *testing.T) {
 				},
 			},
 			assignment: flavorassigner.Assignment{
-				Borrowing: true,
+				Borrowing: 1,
 			},
 		},
 		{
@@ -3315,6 +3378,19 @@ func TestEntryOrdering(t *testing.T) {
 				},
 			},
 		},
+		{
+			Info: workload.Info{
+				Obj: &kueue.Workload{ObjectMeta: metav1.ObjectMeta{
+					Name:              "high_pri_borrowing_more",
+					CreationTimestamp: metav1.NewTime(now.Add(3 * time.Second)),
+				}, Spec: kueue.WorkloadSpec{
+					Priority: ptr.To[int32](1),
+				}},
+			},
+			assignment: flavorassigner.Assignment{
+				Borrowing: 2,
+			},
+		},
 	}
 	inputForOrderingPreemptedWorkloads := []entry{
 		{
@@ -3407,28 +3483,28 @@ func TestEntryOrdering(t *testing.T) {
 			input:            input,
 			prioritySorting:  true,
 			workloadOrdering: workload.Ordering{PodsReadyRequeuingTimestamp: config.EvictionTimestamp},
-			wantOrder:        []string{"new_high_pri", "old", "recently_evicted", "new", "high_pri_borrowing", "old_borrowing", "evicted_borrowing", "new_borrowing"},
+			wantOrder:        []string{"new_high_pri", "old", "recently_evicted", "new", "high_pri_borrowing", "old_borrowing", "evicted_borrowing", "new_borrowing", "high_pri_borrowing_more"},
 		},
 		{
 			name:             "Priority sorting is enabled (default) using pods-ready Creation timestamp",
 			input:            input,
 			prioritySorting:  true,
 			workloadOrdering: workload.Ordering{PodsReadyRequeuingTimestamp: config.CreationTimestamp},
-			wantOrder:        []string{"new_high_pri", "recently_evicted", "old", "new", "high_pri_borrowing", "old_borrowing", "evicted_borrowing", "new_borrowing"},
+			wantOrder:        []string{"new_high_pri", "recently_evicted", "old", "new", "high_pri_borrowing", "old_borrowing", "evicted_borrowing", "new_borrowing", "high_pri_borrowing_more"},
 		},
 		{
 			name:             "Priority sorting is disabled using pods-ready Eviction timestamp",
 			input:            input,
 			prioritySorting:  false,
 			workloadOrdering: workload.Ordering{PodsReadyRequeuingTimestamp: config.EvictionTimestamp},
-			wantOrder:        []string{"old", "recently_evicted", "new", "new_high_pri", "old_borrowing", "evicted_borrowing", "high_pri_borrowing", "new_borrowing"},
+			wantOrder:        []string{"old", "recently_evicted", "new", "new_high_pri", "old_borrowing", "evicted_borrowing", "high_pri_borrowing", "new_borrowing", "high_pri_borrowing_more"},
 		},
 		{
 			name:             "Priority sorting is disabled using pods-ready Creation timestamp",
 			input:            input,
 			prioritySorting:  false,
 			workloadOrdering: workload.Ordering{PodsReadyRequeuingTimestamp: config.CreationTimestamp},
-			wantOrder:        []string{"recently_evicted", "old", "new", "new_high_pri", "old_borrowing", "evicted_borrowing", "high_pri_borrowing", "new_borrowing"},
+			wantOrder:        []string{"recently_evicted", "old", "new", "new_high_pri", "old_borrowing", "evicted_borrowing", "high_pri_borrowing", "new_borrowing", "high_pri_borrowing_more"},
 		},
 		{
 			name:            "Some workloads are preempted; Priority sorting is disabled",
@@ -4149,7 +4225,7 @@ func TestResourcesToReserve(t *testing.T) {
 	cases := []struct {
 		name            string
 		assignmentMode  flavorassigner.FlavorAssignmentMode
-		borrowing       bool
+		borrowing       int
 		assignmentUsage resources.FlavorResourceQuantities
 		cqUsage         resources.FlavorResourceQuantities
 		wantReserved    resources.FlavorResourceQuantities
@@ -4229,7 +4305,7 @@ func TestResourcesToReserve(t *testing.T) {
 		{
 			name:           "Reserved memory cut by nominal+borrowing quota, assignment preempts and borrows",
 			assignmentMode: flavorassigner.Preempt,
-			borrowing:      true,
+			borrowing:      1,
 			assignmentUsage: resources.FlavorResourceQuantities{
 				{Flavor: kueue.ResourceFlavorReference("spot"), Resource: corev1.ResourceMemory}: 50,
 				{Flavor: kueue.ResourceFlavorReference("model-b"), Resource: "gpu"}:              2,
@@ -4248,7 +4324,7 @@ func TestResourcesToReserve(t *testing.T) {
 		{
 			name:           "Reserved memory equal assignment usage, CQ borrowing limit is nil",
 			assignmentMode: flavorassigner.Preempt,
-			borrowing:      true,
+			borrowing:      1,
 			assignmentUsage: resources.FlavorResourceQuantities{
 				{Flavor: kueue.ResourceFlavorReference("on-demand"), Resource: corev1.ResourceMemory}: 50,
 				{Flavor: kueue.ResourceFlavorReference("model-b"), Resource: "gpu"}:                   2,
diff --git a/test/e2e/config/default/kustomization.yaml b/test/e2e/config/default/kustomization.yaml
index 11a455055..dcf86970a 100644
--- a/test/e2e/config/default/kustomization.yaml
+++ b/test/e2e/config/default/kustomization.yaml
@@ -3,7 +3,6 @@ kind: Kustomization
 
 resources:
 - ../../../../config/dev
-- ../../../../config/visibility-apf
 
 replicas:
 - name: kueue-controller-manager
diff --git a/test/integration/singlecluster/controller/admissionchecks/provisioning/provisioning_test.go b/test/integration/singlecluster/controller/admissionchecks/provisioning/provisioning_test.go
index 039ca2bbc..f34ab6872 100644
--- a/test/integration/singlecluster/controller/admissionchecks/provisioning/provisioning_test.go
+++ b/test/integration/singlecluster/controller/admissionchecks/provisioning/provisioning_test.go
@@ -18,7 +18,6 @@ package provisioning
 
 import (
 	"fmt"
-	"time"
 
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/onsi/ginkgo/v2"
@@ -35,7 +34,6 @@ import (
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/constants"
 	"sigs.k8s.io/kueue/pkg/controller/admissionchecks/provisioning"
-	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	"sigs.k8s.io/kueue/pkg/workload"
 	"sigs.k8s.io/kueue/test/integration/framework"
@@ -896,584 +894,6 @@ var _ = ginkgo.Describe("Provisioning", ginkgo.Ordered, ginkgo.ContinueOnFailure
 		})
 	})
 
-	ginkgo.When("Feature KeepQuotaForProvReqRetry is enabled and a workload is using a pending provision admission check with BackOffLimitCount=0", func() {
-		var (
-			ns             *corev1.Namespace
-			wlKey          types.NamespacedName
-			provReqKey     types.NamespacedName
-			ac             *kueue.AdmissionCheck
-			pendingAC      *kueue.AdmissionCheck
-			prc            *kueue.ProvisioningRequestConfig
-			rf             *kueue.ResourceFlavor
-			cq             *kueue.ClusterQueue
-			lq             *kueue.LocalQueue
-			admission      *kueue.Admission
-			createdRequest autoscaling.ProvisioningRequest
-			updatedWl      kueue.Workload
-		)
-		ginkgo.JustBeforeEach(func() {
-			ginkgo.By("Enabling KeepQuotaForProvReqRetry feature", func() {
-				features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.KeepQuotaForProvReqRetry, true)
-			})
-
-			ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "provisioning-")
-
-			prc = baseConfigWithParameters.Clone().RetryLimit(0).Obj()
-
-			util.MustCreate(ctx, k8sClient, prc)
-
-			ac = testing.MakeAdmissionCheck("ac-prov").
-				ControllerName(kueue.ProvisioningRequestControllerName).
-				Parameters(kueue.GroupVersion.Group, "ProvisioningRequestConfig", prc.Name).
-				Obj()
-			util.MustCreate(ctx, k8sClient, ac)
-
-			pendingAC = testing.MakeAdmissionCheck("pending-ac").
-				ControllerName("dummy-controller").
-				Obj()
-			util.MustCreate(ctx, k8sClient, pendingAC)
-
-			rf = testing.MakeResourceFlavor(flavorOnDemand).NodeLabel("ns1", "ns1v").Obj()
-			util.MustCreate(ctx, k8sClient, rf)
-
-			cq = testing.MakeClusterQueue("cluster-queue").
-				ResourceGroup(*testing.MakeFlavorQuotas(flavorOnDemand).
-					Resource(resourceGPU, "5", "5").Obj()).
-				Cohort("cohort").
-				AdmissionChecks(kueue.AdmissionCheckReference(ac.Name), kueue.AdmissionCheckReference(pendingAC.Name)).
-				Obj()
-			util.MustCreate(ctx, k8sClient, cq)
-			gomega.Eventually(func(g gomega.Gomega) {
-				g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(cq), cq)).Should(gomega.Succeed())
-				g.Expect(cq.Status.Conditions).To(gomega.ContainElements(gomega.BeComparableTo(metav1.Condition{
-					Type:    kueue.ClusterQueueActive,
-					Status:  metav1.ConditionFalse,
-					Reason:  "AdmissionCheckInactive",
-					Message: "Can't admit new workloads: references inactive AdmissionCheck(s): [pending-ac].",
-				}, util.IgnoreConditionTimestampsAndObservedGeneration)))
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-			lq = testing.MakeLocalQueue("queue", ns.Name).ClusterQueue(cq.Name).Obj()
-			util.MustCreate(ctx, k8sClient, lq)
-			gomega.Eventually(func(g gomega.Gomega) {
-				g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(lq), lq)).Should(gomega.Succeed())
-				g.Expect(lq.Status.Conditions).To(gomega.ContainElements(gomega.BeComparableTo(metav1.Condition{
-					Type:    kueue.LocalQueueActive,
-					Status:  metav1.ConditionFalse,
-					Reason:  "ClusterQueueIsInactive",
-					Message: "Can't submit new workloads to clusterQueue",
-				}, util.IgnoreConditionTimestampsAndObservedGeneration)))
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-			wl := testing.MakeWorkload("wl", ns.Name).
-				Queue(lq.Name).
-				PodSets(
-					*testing.MakePodSet("ps1", 3).
-						Request(corev1.ResourceCPU, "1").
-						Image("image").
-						Obj(),
-					*testing.MakePodSet("ps2", 6).
-						Request(corev1.ResourceCPU, "500m").
-						Request(customResourceOne, "1").
-						Limit(customResourceOne, "1").
-						Image("image").
-						Obj(),
-				).
-				Annotations(map[string]string{
-					"provreq.kueue.x-k8s.io/ValidUntilSeconds": "0",
-					"invalid-provreq-prefix/Foo":               "Bar"}).
-				Obj()
-			util.MustCreate(ctx, k8sClient, wl)
-			util.SetWorkloadsAdmissionCheck(ctx, k8sClient, wl, kueue.AdmissionCheckReference(pendingAC.Name), kueue.CheckStateReady, false)
-
-			wlKey = client.ObjectKeyFromObject(wl)
-			provReqKey = types.NamespacedName{
-				Namespace: wlKey.Namespace,
-				Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 1),
-			}
-
-			admission = testing.MakeAdmission(cq.Name).
-				PodSets(
-					kueue.PodSetAssignment{
-						Name: "ps1",
-						Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
-							corev1.ResourceCPU: kueue.ResourceFlavorReference(rf.Name),
-						},
-						ResourceUsage: map[corev1.ResourceName]resource.Quantity{
-							corev1.ResourceCPU: resource.MustParse("3"),
-						},
-						Count: ptr.To[int32](3),
-					},
-					kueue.PodSetAssignment{
-						Name: "ps2",
-						Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
-							corev1.ResourceCPU: kueue.ResourceFlavorReference(rf.Name),
-						},
-						ResourceUsage: map[corev1.ResourceName]resource.Quantity{
-							corev1.ResourceCPU: resource.MustParse("2"),
-						},
-						Count: ptr.To[int32](4),
-					},
-				).
-				Obj()
-		})
-
-		ginkgo.AfterEach(func() {
-			gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, cq, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, rf, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, ac, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, pendingAC, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, prc, true)
-		})
-
-		ginkgo.It("Should reject an admission check if a workload is not Admitted, the ProvisioningRequest's condition is set to BookingExpired", func() {
-			ginkgo.By("Setting the admission check to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(ac.Name), kueue.CheckStatePending, false)
-				util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(pendingAC.Name), kueue.CheckStatePending, true)
-			})
-
-			ginkgo.By("Setting the quota reservation to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					gomega.Expect(util.SetQuotaReservation(ctx, k8sClient, &updatedWl, admission)).To(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provisioning request as Provisioned", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).To(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Provisioned,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Provisioned,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking if one admission check is ready, and the other is pending thus workload is not admitted", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:  kueue.AdmissionCheckReference(ac.Name),
-							State: kueue.CheckStateReady,
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:  kueue.AdmissionCheckReference(pendingAC.Name),
-							State: kueue.CheckStatePending,
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					g.Expect(workload.IsAdmitted(&updatedWl)).To(gomega.BeFalse(), "The Workload should not be admitted")
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the ProvisioningRequest as BookingExpired", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.BookingExpired,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.BookingExpired,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking if workload is deactivated, once had Rejected status in the status.admissionCheck[*] field, an event is emitted and a metric is increased", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:    kueue.AdmissionCheckReference(ac.Name),
-							State:   kueue.CheckStatePending,
-							Message: "Reset to Pending after eviction. Previously: Rejected",
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-					g.Expect(workload.IsActive(&updatedWl)).To(gomega.BeFalse(), "The workload should be deactivated")
-
-					ok, err := testing.HasEventAppeared(ctx, k8sClient, corev1.Event{
-						Reason:  "AdmissionCheckRejected",
-						Type:    corev1.EventTypeWarning,
-						Message: fmt.Sprintf("Deactivating workload because AdmissionCheck for %v was Rejected: ", ac.Name),
-					})
-					g.Expect(err).NotTo(gomega.HaveOccurred())
-					g.Expect(ok).To(gomega.BeTrue(), "The event should have appeared")
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-
-					g.Expect(workload.IsEvictedByDeactivation(&updatedWl)).To(gomega.BeTrue(), "The workload should be evicted by deactivation")
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				util.ExpectEvictedWorkloadsTotalMetric(cq.Name, "DeactivatedDueToAdmissionCheck", 1)
-			})
-		})
-	})
-
-	ginkgo.When("FeatureGate KeepQuotaForProvReqRetry is enabled, and a workload is using a provision admission check with  BackoffLimitCount=1", func() {
-		var (
-			ns             *corev1.Namespace
-			wlKey          types.NamespacedName
-			provReqKey     types.NamespacedName
-			ac             *kueue.AdmissionCheck
-			pendingAC      *kueue.AdmissionCheck
-			prc            *kueue.ProvisioningRequestConfig
-			rf             *kueue.ResourceFlavor
-			cq             *kueue.ClusterQueue
-			lq             *kueue.LocalQueue
-			admission      *kueue.Admission
-			createdRequest autoscaling.ProvisioningRequest
-			updatedWl      kueue.Workload
-		)
-		ginkgo.JustBeforeEach(func() {
-			ginkgo.By("Enabling KeepQuotaForProvReqRetry feature", func() {
-				features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.KeepQuotaForProvReqRetry, true)
-			})
-
-			ns = util.CreateNamespaceFromPrefixWithLog(ctx, k8sClient, "provisioning-")
-			prc = baseConfigWithParameters.Clone().RetryLimit(1).BaseBackoff(1).Obj()
-			util.MustCreate(ctx, k8sClient, prc)
-
-			ac = testing.MakeAdmissionCheck("ac-prov").
-				ControllerName(kueue.ProvisioningRequestControllerName).
-				Parameters(kueue.GroupVersion.Group, "ProvisioningRequestConfig", prc.Name).
-				Obj()
-			util.MustCreate(ctx, k8sClient, ac)
-
-			pendingAC = testing.MakeAdmissionCheck("pending-ac").
-				ControllerName("dummy-controller").
-				Obj()
-			util.MustCreate(ctx, k8sClient, pendingAC)
-
-			rf = testing.MakeResourceFlavor("rf1").Label("ns1", "ns1v").Obj()
-			util.MustCreate(ctx, k8sClient, rf)
-
-			cq = testing.MakeClusterQueue("cluster-queue").
-				ResourceGroup(*testing.MakeFlavorQuotas(flavorOnDemand).
-					Resource(resourceGPU, "5", "5").Obj()).
-				Cohort("cohort").
-				AdmissionChecks(kueue.AdmissionCheckReference(ac.Name), kueue.AdmissionCheckReference(pendingAC.Name)).
-				Obj()
-			util.MustCreate(ctx, k8sClient, cq)
-			lq = testing.MakeLocalQueue("queue", ns.Name).ClusterQueue(cq.Name).Obj()
-			util.MustCreate(ctx, k8sClient, lq)
-			wl := testing.MakeWorkload("wl", ns.Name).
-				Queue(lq.Name).
-				PodSets(
-					*testing.MakePodSet("ps1", 3).
-						Request(corev1.ResourceCPU, "1").
-						Image("image").
-						Obj(),
-					*testing.MakePodSet("ps2", 6).
-						Request(corev1.ResourceCPU, "500m").
-						Request(customResourceOne, "1").
-						Limit(customResourceOne, "1").
-						Image("image").
-						Obj(),
-				).
-				Obj()
-			util.MustCreate(ctx, k8sClient, wl)
-			util.SetWorkloadsAdmissionCheck(ctx, k8sClient, wl, kueue.AdmissionCheckReference(pendingAC.Name), kueue.CheckStateReady, false)
-
-			wlKey = client.ObjectKeyFromObject(wl)
-			provReqKey = types.NamespacedName{
-				Namespace: wlKey.Namespace,
-				Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 1),
-			}
-
-			admission = testing.MakeAdmission(cq.Name).
-				PodSets(
-					kueue.PodSetAssignment{
-						Name: "ps1",
-						Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
-							corev1.ResourceCPU: kueue.ResourceFlavorReference(rf.Name),
-						},
-						ResourceUsage: map[corev1.ResourceName]resource.Quantity{
-							corev1.ResourceCPU: resource.MustParse("3"),
-						},
-						Count: ptr.To[int32](3),
-					},
-					kueue.PodSetAssignment{
-						Name: "ps2",
-						Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
-							corev1.ResourceCPU: kueue.ResourceFlavorReference(rf.Name),
-						},
-						ResourceUsage: map[corev1.ResourceName]resource.Quantity{
-							corev1.ResourceCPU: resource.MustParse("2"),
-						},
-						Count: ptr.To[int32](4),
-					},
-				).
-				Obj()
-		})
-
-		ginkgo.AfterEach(func() {
-			gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, cq, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, rf, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, ac, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, pendingAC, true)
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, prc, true)
-		})
-
-		ginkgo.It("Should set check as Pending after the first ProvisioningRequest fails, then as Ready if the second Provisioning request succeeds", func() {
-			ginkgo.By("Setting the admission check to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(ac.Name), kueue.CheckStatePending, false)
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the quota reservation to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					g.Expect(util.SetQuotaReservation(ctx, k8sClient, &updatedWl, admission)).To(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provision request-1 as Failed", func() {
-				provReqKey := types.NamespacedName{
-					Namespace: wlKey.Namespace,
-					Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 1),
-				}
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Failed,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Failed,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking the admission check is pending", func() {
-				// use consistently with short interval to make sure it does not
-				// flip to a short period of time.
-				gomega.Consistently(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					state := workload.FindAdmissionCheck(updatedWl.Status.AdmissionChecks, kueue.AdmissionCheckReference(ac.Name))
-					g.Expect(state).NotTo(gomega.BeNil())
-					g.Expect(state.State).To(gomega.Equal(kueue.CheckStatePending))
-				}, time.Second, 10*time.Millisecond).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provision request-2 as Provisioned", func() {
-				provReqKey := types.NamespacedName{
-					Namespace: wlKey.Namespace,
-					Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 2),
-				}
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Provisioned,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Provisioned,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking the admission check is ready", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					state := workload.FindAdmissionCheck(updatedWl.Status.AdmissionChecks, kueue.AdmissionCheckReference(ac.Name))
-					g.Expect(state).NotTo(gomega.BeNil())
-					g.Expect(state.State).To(gomega.Equal(kueue.CheckStateReady))
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-		})
-
-		ginkgo.It("Should set check as Rejected, if every ProvisioningRequest retry fails", func() {
-			ginkgo.By("Setting the admission check to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(ac.Name), kueue.CheckStatePending, false)
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the quota reservation to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					g.Expect(util.SetQuotaReservation(ctx, k8sClient, &updatedWl, admission)).To(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provision request-1 as Failed", func() {
-				provReqKey := types.NamespacedName{
-					Namespace: wlKey.Namespace,
-					Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 1),
-				}
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Failed,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Failed,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking the admission check is pending", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					state := workload.FindAdmissionCheck(updatedWl.Status.AdmissionChecks, kueue.AdmissionCheckReference(ac.Name))
-					g.Expect(state).NotTo(gomega.BeNil())
-					g.Expect(state.State).To(gomega.Equal(kueue.CheckStatePending))
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provision request-2 as Failed", func() {
-				provReqKey := types.NamespacedName{
-					Namespace: wlKey.Namespace,
-					Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 2),
-				}
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Failed,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Failed,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking if workload is deactivated, once had Rejected status in the status.admissionCheck[*] field, an event is emitted and a metric is increased", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:    kueue.AdmissionCheckReference(ac.Name),
-							State:   kueue.CheckStatePending,
-							Message: "Reset to Pending after eviction. Previously: Rejected",
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-					g.Expect(workload.IsActive(&updatedWl)).To(gomega.BeFalse())
-
-					ok, err := testing.HasEventAppeared(ctx, k8sClient, corev1.Event{
-						Reason:  "AdmissionCheckRejected",
-						Type:    corev1.EventTypeWarning,
-						Message: fmt.Sprintf("Deactivating workload because AdmissionCheck for %v was Rejected: ", ac.Name),
-					})
-					g.Expect(err).NotTo(gomega.HaveOccurred())
-					g.Expect(ok).To(gomega.BeTrue())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-
-					g.Expect(workload.IsEvictedByDeactivation(&updatedWl)).To(gomega.BeTrue())
-					util.ExpectEvictedWorkloadsTotalMetric(cq.Name, "DeactivatedDueToAdmissionCheck", 1)
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-		})
-
-		ginkgo.It("Should set a check as Pending if a workload is not Admitted, and the ProvisioningRequest's condition is set to BookingExpired", func() {
-			ginkgo.By("Setting the admission check to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(ac.Name), kueue.CheckStatePending, false)
-				util.SetWorkloadsAdmissionCheck(ctx, k8sClient, &updatedWl, kueue.AdmissionCheckReference(pendingAC.Name), kueue.CheckStatePending, true)
-			})
-
-			ginkgo.By("Setting the quota reservation to the workload", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).Should(gomega.Succeed())
-					g.Expect(util.SetQuotaReservation(ctx, k8sClient, &updatedWl, admission)).To(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the provisioning request as Provisioned", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).To(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.Provisioned,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.Provisioned,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking if one admission check is ready, and the other is pending thus workload is not admitted", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:  kueue.AdmissionCheckReference(ac.Name),
-							State: kueue.CheckStateReady,
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:  kueue.AdmissionCheckReference(pendingAC.Name),
-							State: kueue.CheckStatePending,
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates"))))
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					g.Expect(workload.IsAdmitted(&updatedWl)).To(gomega.BeFalse(), "The workload shouldn't be admitted")
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Setting the ProvisioningRequest as BookingExpired", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).Should(gomega.Succeed())
-					apimeta.SetStatusCondition(&createdRequest.Status.Conditions, metav1.Condition{
-						Type:   autoscaling.BookingExpired,
-						Status: metav1.ConditionTrue,
-						Reason: autoscaling.BookingExpired,
-					})
-					g.Expect(k8sClient.Status().Update(ctx, &createdRequest)).Should(gomega.Succeed())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-
-			ginkgo.By("Checking if the admission check is pending and the request is retried", func() {
-				gomega.Eventually(func(g gomega.Gomega) {
-					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
-					g.Expect(updatedWl.Status.AdmissionChecks).To(gomega.ContainElement(gomega.BeComparableTo(
-						kueue.AdmissionCheckState{
-							Name:  kueue.AdmissionCheckReference(ac.Name),
-							State: kueue.CheckStatePending,
-						},
-						cmpopts.IgnoreFields(kueue.AdmissionCheckState{}, "LastTransitionTime", "PodSetUpdates", "Message"))))
-
-					provReqKey = types.NamespacedName{
-						Namespace: wlKey.Namespace,
-						Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 2),
-					}
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).To(gomega.Succeed())
-					provReqKey = types.NamespacedName{
-						Namespace: wlKey.Namespace,
-						Name:      provisioning.ProvisioningRequestName(wlKey.Name, kueue.AdmissionCheckReference(ac.Name), 1),
-					}
-					g.Expect(k8sClient.Get(ctx, provReqKey, &createdRequest)).To(testing.BeNotFoundError())
-				}, util.Timeout, util.Interval).Should(gomega.Succeed())
-			})
-		})
-	})
-
 	ginkgo.When("Workload uses a provision admission check with BackoffLimitCount=1", func() {
 		var (
 			ns             *corev1.Namespace
diff --git a/test/integration/singlecluster/scheduler/preemption_test.go b/test/integration/singlecluster/scheduler/preemption_test.go
index 1cbe0ba5a..237c8c344 100644
--- a/test/integration/singlecluster/scheduler/preemption_test.go
+++ b/test/integration/singlecluster/scheduler/preemption_test.go
@@ -28,6 +28,7 @@ import (
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	kueuealpha "sigs.k8s.io/kueue/apis/kueue/v1alpha1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/features"
@@ -910,4 +911,81 @@ var _ = ginkgo.Describe("Preemption", func() {
 			})
 		})
 	})
+	ginkgo.Context("In a multi-level cohort", func() {
+		var rootCohort, guaranteedCohort *kueuealpha.Cohort
+		var bestEffortCQ, guaranteedCQ *kueue.ClusterQueue
+		var defaultFlavor *kueue.ResourceFlavor
+
+		ginkgo.BeforeEach(func() {
+			defaultFlavor = testing.MakeResourceFlavor("default").Obj()
+			gomega.Expect(k8sClient.Create(ctx, defaultFlavor)).To(gomega.Succeed())
+
+			rootCohort = testing.MakeCohort("root").Obj()
+			gomega.Expect(k8sClient.Create(ctx, rootCohort)).To(gomega.Succeed())
+			guaranteedCohort = testing.MakeCohort("guaranteed").Parent("root").
+				ResourceGroup(
+					*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "3").Obj(),
+				).Obj()
+			gomega.Expect(k8sClient.Create(ctx, guaranteedCohort)).To(gomega.Succeed())
+			bestEffortCQ = testing.MakeClusterQueue("best-effort").
+				Cohort("root").
+				ResourceGroup(
+					*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "0").Obj(),
+				).
+				Preemption(kueue.ClusterQueuePreemption{
+					WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+					ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+				}).
+				Obj()
+			gomega.Expect(k8sClient.Create(ctx, bestEffortCQ)).To(gomega.Succeed())
+			guaranteedCQ = testing.MakeClusterQueue("guaranteed").
+				Cohort("guaranteed").ResourceGroup(
+				*testing.MakeFlavorQuotas("default").Resource(corev1.ResourceCPU, "0").Obj(),
+			).
+				Preemption(kueue.ClusterQueuePreemption{
+					WithinClusterQueue:  kueue.PreemptionPolicyLowerPriority,
+					ReclaimWithinCohort: kueue.PreemptionPolicyAny,
+				}).
+				Obj()
+			gomega.Expect(k8sClient.Create(ctx, guaranteedCQ)).To(gomega.Succeed())
+		})
+		ginkgo.It("workloads in guaranteed cq should have preferential access to the resources", func() {
+			var bestEffortLQ *kueue.LocalQueue
+			var guaranteedLQ *kueue.LocalQueue
+			var bestEffortWl, guaranteedWl *kueue.Workload
+
+			ginkgo.By("Create local queues", func() {
+				bestEffortLQ = testing.MakeLocalQueue("best-effort-lq", ns.Name).ClusterQueue(bestEffortCQ.Name).Obj()
+				gomega.Expect(k8sClient.Create(ctx, bestEffortLQ)).Should(gomega.Succeed())
+				guaranteedLQ = testing.MakeLocalQueue("guaranteed-lq", ns.Name).ClusterQueue(guaranteedCQ.Name).Obj()
+				gomega.Expect(k8sClient.Create(ctx, guaranteedLQ)).Should(gomega.Succeed())
+			})
+
+			ginkgo.By("Create a workload in bestEffortCQ and await for admission", func() {
+				bestEffortWl = testing.MakeWorkload("best-effort-wl", ns.Name).
+					Queue(bestEffortLQ.Name).
+					Priority(highPriority).
+					Request(corev1.ResourceCPU, "3").
+					Obj()
+				gomega.Expect(k8sClient.Create(ctx, bestEffortWl)).To(gomega.Succeed())
+				util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, bestEffortWl)
+			})
+
+			ginkgo.By("Create a workload in guaranteedCQ", func() {
+				guaranteedWl = testing.MakeWorkload("guaranteed-wl", ns.Name).
+					Queue(guaranteedLQ.Name).
+					Priority(midPriority).
+					Request(corev1.ResourceCPU, "3").
+					Obj()
+				gomega.Expect(k8sClient.Create(ctx, guaranteedWl)).To(gomega.Succeed())
+			})
+
+			ginkgo.By("Await for preemption of the workload in bestEffortCQ and admission of guaranteedWL", func() {
+				util.ExpectWorkloadsToBePreempted(ctx, k8sClient, bestEffortWl)
+				util.FinishEvictionForWorkloads(ctx, k8sClient, bestEffortWl)
+				util.ExpectWorkloadsToBePending(ctx, k8sClient, bestEffortWl)
+				util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, guaranteedWl)
+			})
+		})
+	})
 })
diff --git a/test/integration/singlecluster/scheduler/workload_controller_test.go b/test/integration/singlecluster/scheduler/workload_controller_test.go
index 58555997e..489ae502e 100644
--- a/test/integration/singlecluster/scheduler/workload_controller_test.go
+++ b/test/integration/singlecluster/scheduler/workload_controller_test.go
@@ -140,7 +140,7 @@ var _ = ginkgo.Describe("Workload controller with scheduler", func() {
 					g.Expect(workload.HasQuotaReservation(&updatedWl)).Should(gomega.BeTrue(), "should have quota reservation")
 
 					checks := slices.Map(updatedWl.Status.AdmissionChecks, func(c *kueue.AdmissionCheckState) kueue.AdmissionCheckReference { return c.Name })
-					g.Expect(checks).Should(gomega.ConsistOf("check1", "check2"))
+					g.Expect(checks).Should(gomega.ConsistOf(kueue.AdmissionCheckReference("check1"), kueue.AdmissionCheckReference("check2")))
 				}, util.Timeout, util.Interval).Should(gomega.Succeed())
 				gomega.Expect(workload.IsAdmitted(&updatedWl)).To(gomega.BeFalse())
 			})
@@ -157,7 +157,7 @@ var _ = ginkgo.Describe("Workload controller with scheduler", func() {
 					g.Expect(k8sClient.Update(ctx, &createdQueue)).To(gomega.Succeed())
 					g.Expect(k8sClient.Get(ctx, wlKey, &updatedWl)).To(gomega.Succeed())
 					checks := slices.Map(updatedWl.Status.AdmissionChecks, func(c *kueue.AdmissionCheckState) kueue.AdmissionCheckReference { return c.Name })
-					g.Expect(checks).Should(gomega.ConsistOf("check1", "check3"))
+					g.Expect(checks).Should(gomega.ConsistOf(kueue.AdmissionCheckReference("check1"), kueue.AdmissionCheckReference("check3")))
 				}, 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 1c8e2195c..e4835ebba 100644
--- a/test/integration/singlecluster/tas/tas_test.go
+++ b/test/integration/singlecluster/tas/tas_test.go
@@ -1214,6 +1214,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					ResourceGroup(*testing.MakeFlavorQuotas(tasFlavor.Name).Resource(corev1.ResourceCPU, "5").Obj()).
 					Obj()
 				util.MustCreate(ctx, k8sClient, clusterQueue)
+				util.ExpectClusterQueuesToBeActive(ctx, k8sClient, clusterQueue)
 
 				localQueue = testing.MakeLocalQueue("local-queue", ns.Name).ClusterQueue(clusterQueue.Name).Obj()
 				util.MustCreate(ctx, k8sClient, localQueue)
@@ -1285,6 +1286,74 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectPendingWorkloadsMetric(clusterQueue, 0, 0)
 				})
 			})
+
+			ginkgo.It("should admit workload when node gets required label added", func() {
+				var (
+					wl1 *kueue.Workload
+				)
+				customLabelKey := "custom-label-key-1"
+				customLabelCorrectValue := "value-1"
+				customLabelWrongValue := "value-2"
+
+				ginkgo.By("creating a node missing the required label", func() {
+					nodes = []corev1.Node{
+						*testingnode.MakeNode("node-missing-label").
+							Label("node-group", "tas").
+							Label(testing.DefaultBlockTopologyLevel, "b1").
+							Label(testing.DefaultRackTopologyLevel, "r1").
+							Label(corev1.LabelHostname, "node-missing-label").
+							StatusAllocatable(corev1.ResourceList{
+								corev1.ResourceCPU:    resource.MustParse("1"),
+								corev1.ResourceMemory: resource.MustParse("1Gi"),
+								corev1.ResourcePods:   resource.MustParse("10"),
+							}).
+							Ready().
+							Obj(),
+					}
+					util.CreateNodesWithStatus(ctx, k8sClient, nodes)
+				})
+
+				ginkgo.By("creating a workload requiring the missing label via NodeSelector", func() {
+					wl1 = testing.MakeWorkload("wl-needs-label", ns.Name).
+						Queue(localQueue.Name).
+						PodSets(*testing.MakePodSet("main", 1).
+							NodeSelector(map[string]string{customLabelKey: customLabelCorrectValue}).
+							Request(corev1.ResourceCPU, "1").
+							Obj()).
+						Obj()
+					util.MustCreate(ctx, k8sClient, wl1)
+				})
+
+				ginkgo.By("verify the workload is inadmissible due to missing label", func() {
+					util.ExpectWorkloadsToBePending(ctx, k8sClient, wl1)
+					util.ExpectPendingWorkloadsMetric(clusterQueue, 0, 1)
+				})
+
+				ginkgo.By("Add a label to the node with correct key but wrong value", func() {
+					nodeToUpdate := &corev1.Node{}
+					gomega.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: nodes[0].Name}, nodeToUpdate)).Should(gomega.Succeed())
+					nodeToUpdate.Labels[customLabelKey] = customLabelWrongValue
+					gomega.Expect(k8sClient.Update(ctx, nodeToUpdate)).Should(gomega.Succeed())
+				})
+
+				ginkgo.By("verify the workload is inadmissible due to wrong value in the label", func() {
+					util.ExpectWorkloadsToBePending(ctx, k8sClient, wl1)
+					util.ExpectPendingWorkloadsMetric(clusterQueue, 0, 1)
+				})
+
+				ginkgo.By("Add the correct label to the node", func() {
+					nodeToUpdate := &corev1.Node{}
+					gomega.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: nodes[0].Name}, nodeToUpdate)).Should(gomega.Succeed())
+					nodeToUpdate.Labels[customLabelKey] = customLabelCorrectValue
+					gomega.Expect(k8sClient.Update(ctx, nodeToUpdate)).Should(gomega.Succeed())
+				})
+
+				ginkgo.By("verify the workload gets admitted after label is added", func() {
+					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
+					util.ExpectReservingActiveWorkloadsMetric(clusterQueue, 1)
+					util.ExpectPendingWorkloadsMetric(clusterQueue, 0, 0)
+				})
+			})
 		})
 	})