Repository: kubernetes-sigs/kueue

Issue:
[flaky workload slicing unit test] sigs.k8s.io/kueue/pkg/workloadslicing: TestFinish/AlreadyFinishedWorkload
**What happened**:

failure on unrelated branch: https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_kueue/7075/pull-kueue-test-unit-main/1973017516351426560

**What you expected to happen**:
no failure
**How to reproduce it (as minimally and precisely as possible)**:
ci
**Anything else we need to know?**:
```
sigs.k8s.io/kueue/pkg/workloadslicing: TestFinish/AlreadyFinishedWorkload expand_less
Run #0: Failed expand_less	0s
{Failed  === RUN   TestFinish/AlreadyFinishedWorkload
    workloadslicing_test.go:377: Deactivated() (-want,+got):
          &v1beta1.Workload{
          	TypeMeta:   {},
          	ObjectMeta: {Name: "test", Namespace: "default", ResourceVersion: "1", CreationTimestamp: {Time: s"2025-09-30 13:48:12 +0000 UTC"}, ...},
          	Spec:       {PodSets: {{Name: "main", Template: {Spec: {Containers: {{Name: "c", Resources: {Requests: {s"cpu": {i: {...}, s: "100m", Format: "DecimalSI"}}}}}, RestartPolicy: "Never"}}, Count: 1}}},
          	Status: v1beta1.WorkloadStatus{
          		Conditions: []v1.Condition{
          			{
          				Type:               "Finished",
          				Status:             "True",
          				ObservedGeneration: 0,
        - 				LastTransitionTime: v1.Time{Time: s"2025-09-30 13:48:13.013682336 +0000 UTC m=+1.677315711"},
        + 				LastTransitionTime: v1.Time{Time: s"2025-09-30 13:48:12 +0000 UTC"},
          				Reason:             "ByTest",
          				Message:            "Finished by test",
          			},
          		},
          		Admission:    nil,
          		RequeueState: nil,
          		... // 8 identical fields
          	},
          }
--- FAIL: TestFinish/AlreadyFinishedWorkload (0.02s)
}
```

Discussion:
@mimowo: /kind flake 
looks very similar to https://github.com/kubernetes-sigs/kueue/pull/7076
cc @mszadkow @ichekrygin ptal@mszadkow: /assign

Solution patch:
diff --git a/CHANGELOG/CHANGELOG-0.12.md b/CHANGELOG/CHANGELOG-0.12.md
index 57c1f137e..f90dc2358 100644
--- a/CHANGELOG/CHANGELOG-0.12.md
+++ b/CHANGELOG/CHANGELOG-0.12.md
@@ -1,3 +1,20 @@
+## v0.12.10
+
+Changes since `v0.12.9`:
+
+## Changes by Kind
+
+### Bug or Regression
+
+- FS: Validate FairSharing.Weight against small values which lose precision (0 < value <= 10^-9) (#7007, @gabesaba)
+- Fix the bug for the StatefulSet integration which would occasionally cause a StatefulSet
+  to be stuck without workload after renaming the "queue-name" label. (#7038, @IrvingMg)
+- Fix the bug that a workload going repeatedly via the preemption and re-admission cycle would accumulate the
+  "Previously" prefix in the condition message, eg: "Previously: Previously: Previously: Preempted to accommodate a workload ...". (#6875, @amy)
+- Fixed bug where internal cert manager assumed that the helm installation name was kueue. (#6916, @cmtly)
+- Helm: Fixed bug where webhook configurations assumed a helm install name as "kueue". (#6923, @cmtly)
+- Pod-integration now correctly handles pods stuck in the Terminating state within pod groups, preventing them from being counted as active and avoiding blocked quota release. (#6893, @ichekrygin)
+
 ## v0.12.9
 
 Changes since `v0.12.8`:
diff --git a/CHANGELOG/CHANGELOG-0.13.md b/CHANGELOG/CHANGELOG-0.13.md
index 3f1648681..bfb533775 100644
--- a/CHANGELOG/CHANGELOG-0.13.md
+++ b/CHANGELOG/CHANGELOG-0.13.md
@@ -1,3 +1,43 @@
+## v0.13.5
+
+Changes since `v0.13.4`:
+
+## Changes by Kind
+
+### Feature
+
+- KueueViz: Enhancing the following endpoint customizations and optimizations:
+    - The frontend and backend ingress no longer have hardcoded NGINX annotations. You can now set your own annotations in Helm’s values.yaml using kueueViz.backend.ingress.annotations and kueueViz.frontend.ingress.annotations
+    - The Ingress resources for KueueViz frontend and backend no longer require hardcoded TLS. You can now choose to use HTTP only by not providing kueueViz.backend.ingress.tlsSecretName and kueueViz.frontend.ingress.tlsSecretName
+    - You can set environment variables like KUEUEVIZ_ALLOWED_ORIGINS directly from values.yaml using kueueViz.backend.env (#6934, @Smuger)
+
+### Bug or Regression
+
+- ElasticJobs: workloads correctly trigger workload preemption in response to a scale-up event. (#6973, @ichekrygin)
+- FS: Fix the following FairSharing bugs:
+    - Incorrect DominantResourceShare caused by rounding (large quotas or high FairSharing weight)
+    - Preemption loop caused by zero FairSharing weight (#6994, @gabesaba)
+- FS: Validate FairSharing.Weight against small values which lose precision (0 < value <= 10^-9) (#7008, @gabesaba)
+- Fix bug in workload usage removal simulation that results in inaccurate flavor assignment (#7084, @gabesaba)
+- Fix the bug for the StatefulSet integration which would occasionally cause a StatefulSet
+  to be stuck without workload after renaming the "queue-name" label. (#7037, @IrvingMg)
+- Fix the bug that a workload going repeatedly via the preemption and re-admission cycle would accumulate the
+  "Previously" prefix in the condition message, eg: "Previously: Previously: Previously: Preempted to accommodate a workload ...". (#6874, @amy)
+- HC: When multiple borrowing flavors are available, prefer the flavor which
+  results in borrowing more locally (closer to the ClusterQueue, further from the root Cohort).
+
+  This fixes the scenario where a flavor would be selected which required borrowing
+  from the root Cohort in one flavor, while in a second flavor, quota was
+  available from the nearest parent Cohort. (#7042, @gabesaba)
+- Helm: Fix a bug where the internal cert manager assumed that the helm installation name is 'kueue'. (#6917, @cmtly)
+- Helm: Fixed bug where webhook configurations assumed a helm install name as "kueue". (#6924, @cmtly)
+- Pod-integration now correctly handles pods stuck in the Terminating state within pod groups, preventing them from being counted as active and avoiding blocked quota release. (#6892, @ichekrygin)
+- TAS: Fix the scenario when Node Hot Swap cannot find a replacement. In particular, if slices are used
+  they could result in generating invalid assignment, resulting in panic from TopologyUngater.
+  Now, such a workload is evicted. (#6927, @mbobrovskyi)
+- TAS: Node Hot Swap allows replacing a node for workloads using PodSet slices,
+  ie. when the `kueue.x-k8s.io/podset-slice-size` annotation is used. (#6989, @pajakd)
+
 ## v0.13.4
 
 Changes since `v0.13.3`:
diff --git a/CHANGELOG/CHANGELOG-0.14.md b/CHANGELOG/CHANGELOG-0.14.md
new file mode 100644
index 000000000..628798420
--- /dev/null
+++ b/CHANGELOG/CHANGELOG-0.14.md
@@ -0,0 +1,201 @@
+Changes since `v0.13.0`:
+
+## Urgent Upgrade Notes 
+
+### (No, really, you MUST read this before you upgrade)
+
+- ProvisioningRequest: Remove setting deprecated ProvisioningRequest annotations on Kueue-managed Pods:
+  - cluster-autoscaler.kubernetes.io/consume-provisioning-request
+  - cluster-autoscaler.kubernetes.io/provisioning-class-name
+  
+  If you are implementing a ProvisioningRequest reconciler used by Kueue you should
+  make sure the new annotations are supported:
+  - autoscaling.x-k8s.io/consume-provisioning-request
+  - autoscaling.x-k8s.io/provisioning-class-name (#6381, @kannon92)
+ - Rename kueue-metrics-certs to kueue-metrics-cert cert-manager.io/v1 Certificate name in cert-manager manifests when installing Kueue using the Kustomize configuration.
+  
+  If you're using cert-manager and have deployed Kueue using the Kustomize configuration, you must delete the existing kueue-metrics-certs cert-manager.io/v1 Certificate before applying the new changes to avoid conflicts. (#6345, @mbobrovskyi)
+ - Replace "DeactivatedXYZ" "reason" label values with "Deactivated" and introduce "underlying_cause" label to the following metrics:
+  - "pods_ready_to_evicted_time_seconds"
+  - "evicted_workloads_total"
+  - "local_queue_evicted_workloads_total"
+  - "evicted_workloads_once_total"
+  
+  If you rely on the "DeactivatedXYZ" "reason" label values, you can migrate to the "Deactivated" "reason" label value and the following "underlying_cause" label values:
+  - ""
+  - "WaitForStart"
+  - "WaitForRecovery"
+  - "AdmissionCheck"
+  - "MaximumExecutionTimeExceeded"
+  - "RequeuingLimitExceeded" (#6590, @mykysha)
+ - TAS: Enforce a stricter value of the `kueue.x-k8s.io/podset-group-name` annotation in the creation webhook.
+  
+  Make sure the values of the `kueue.x-k8s.io/podset-group-name` annotation are not numbers.` (#6708, @kshalot)
+ 
+## Upgrading steps
+
+### 1. Back Up Topology Resources (skip if you are not using Topology API):
+
+kubectl get topologies.kueue.x-k8s.io -o yaml > topologies.yaml
+
+### 2. Update apiVersion in Backup File (skip if not using Topology API):
+Replace `v1alpha1` with `v1beta1` in topologies.yaml for all resources:
+
+sed -i -e 's/v1alpha1/v1beta1/g' topologies.yaml
+
+### 3. Delete Old CRDs:
+
+kubectl delete crd topologies.kueue.x-k8s.io
+
+### 4. Remove Finalizers from Topologies (skip if you are not using Topology API):
+
+kubectl get topology.kueue.x-k8s.io -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | while read -r name; do
+  kubectl patch topology.kueue.x-k8s.io "$name" -p '{"metadata":{"finalizers":[]}}' --type='merge'
+done
+
+### 5. Install Kueue v0.14.0:
+Follow the instructions [here](https://kueue.sigs.k8s.io/docs/installation/#install-a-released-version) to install.
+
+### 6. Restore Topology Resources (skip if not using Topology API):
+
+kubectl apply -f topologies.yaml
+
+## Changes by Kind
+
+### Deprecation
+
+- Stop serving the QueueVisibility feature, but keep APIs (`.status.pendingWorkloadsStatus`) to avoid breaking changes.
+  
+  If you rely on the QueueVisibility feature (`.status.pendingWorkloadsStatus` in the ClusterQueue), you must migrate to VisibilityOndDemand 
+  (https://kueue.sigs.k8s.io/docs/tasks/manage/monitor_pending_workloads/pending_workloads_on_demand). (#6631, @vladikkuzn)
+
+### API Change
+
+- TAS: Graduated TopologyAwareScheduling to Beta. (#6830, @mbobrovskyi)
+- TAS: Support multiple nodes for failure handling by ".status.unhealthyNodes" in Workload. The "alpha.kueue.x-k8s.io/node-to-replace" annotation is no longer used (#6648, @pajakd)
+
+### Feature
+
+- Add an alpha integration for Kubeflow Trainer to Kueue. (#6597, @kaisoz)
+- Add an exponential backoff for the TAS scheduler second pass. (#6753, @mykysha)
+- Added priority_class label for kueue_local_queue_admitted_workloads_total metric. (#6845, @vladikkuzn)
+- Added priority_class label for kueue_local_queue_evicted_workloads_total metric (#6898, @vladikkuzn)
+- Added priority_class label for kueue_local_queue_quota_reserved_workloads_total metric. (#6897, @vladikkuzn)
+- Added priority_class label for the following metrics:
+  - kueue_admitted_workloads_total
+  - kueue_evicted_workloads_total
+  - kueue_evicted_workloads_once_total
+  - kueue_quota_reserved_workloads_total
+  - kueue_admission_wait_time_seconds
+  - kueue_quota_reserved_wait_time_seconds
+  - kueue_admission_checks_wait_time_seconds (#6951, @mbobrovskyi)
+- Added priority_class to kueue_local_queue_admission_checks_wait_time_seconds (#6902, @vladikkuzn)
+- Added priority_class to kueue_local_queue_admission_wait_time_seconds (#6899, @vladikkuzn)
+- Added priority_class to kueue_local_queue_quota_reserved_wait_time_seconds (#6900, @vladikkuzn)
+- Added workload_priority_class label for optional metrics (if waitForPodsReady is enabled):
+  
+  - kueue_ready_wait_time_seconds (Histogram)
+  - kueue_admitted_until_ready_wait_time_seconds (Histogram)
+  - kueue_local_queue_ready_wait_time_seconds (Histogram)
+  - kueue_local_queue_admitted_until_ready_wait_time_seconds (Histogram) (#6944, @IrvingMg)
+- DRA: Alpha support for Dynamic Resource Allocation in Kueue. (#5873, @alaypatel07)
+- ElasticJobs: Support in-tree RayAutoscaler for RayCluster (#6662, @VassilisVassiliadis)
+- KueueViz: Enhancing the following endpoint customizations and optimizations:
+  - The frontend and backend ingress no longer have hardcoded NGINX annotations. You can now set your own annotations in Helm’s values.yaml using kueueViz.backend.ingress.annotations and kueueViz.frontend.ingress.annotations
+  - The Ingress resources for KueueViz frontend and backend no longer require hardcoded TLS. You can now choose to use HTTP only by not providing kueueViz.backend.ingress.tlsSecretName and kueueViz.frontend.ingress.tlsSecretName
+  - You can set environment variables like KUEUEVIZ_ALLOWED_ORIGINS directly from values.yaml using kueueViz.backend.env (#6682, @Smuger)
+- MultiKueue: Support external frameworks.
+  Introduced a generic MultiKueue adapter to support external, custom Job-like workloads. This allows users to integrate custom Job-like CRDs (e.g., Tekton PipelineRuns) with MultiKueue for resource management across multiple clusters. This feature is guarded by the `MultiKueueGenericJobAdapter` feature gate. (#6760, @khrm)
+- Multikueue × ElasticJobs: The elastic `batchv1/Job` supports MultiKueue. (#6445, @ichekrygin)
+- ProvisioningRequest: Graduate ProvisioningACC feature to GA (#6382, @kannon92)
+- TAS: Graduated to Beta the following feature gates responsible for enabling and default configuration of the Node Hot Swap mechanism: 
+  TASFailedNodeReplacement, TASFailedNodeReplacementFailFast, TASReplaceNodeOnPodTermination. (#6890, @mbobrovskyi)
+- TAS: Implicit mode schedules consecutive indexes as close as possible (rank-ordering). (#6615, @PBundyra)
+- TAS: introduce validation against using PodSet grouping and PodSet slicing for the same PodSet, 
+  which is currently not supported. More precisely the `kueue.x-k8s.io/podset-group-name` annotation
+  cannot be set along with any of: `kueue.x-k8s.io/podset-slice-size`, `kueue.x-k8s.io/podset-slice-required-topology`. (#7051, @kshalot)
+- The following limits for ClusterQueue quota specification have been relaxed:
+  - the number of Flavors per ResourceGroup is increased from 16 to 64
+  - the number of Resources per Flavor, within a ResourceGroup, is increased from 16 to 64
+  
+  We also provide the following additional limits:
+  - the total number of Flavors across all ResourceGroups is <= 256
+  - the total number of Resources across all ResourceGroups is <= 256
+  - the total number of (Flavor, Resource) pairs within a ResourceGroup is <= 512 (#6906, @LarsSven)
+- Visibility API: Adds support for Securing APIService. (#6798, @MaysaMacedo)
+- WorkloadRequestUseMergePatch: allows switching the Status Patch type from Apply to Merge for admission-related patches. (#6765, @mszadkow)
+
+### Bug or Regression
+
+- AFS: Fixed kueue-controller-manager crash when enabled AdmissionFairSharing feature gate without AdmissionFairSharing config. (#6670, @mbobrovskyi)
+- ElasticJobs: Fix the bug for the ElasticJobsViaWorkloadSlices feature where in case of Job resize followed by eviction
+  of the "old" workload, the newly created workload could get admitted along with the "old" workload.
+  The two workloads would overcommit the quota. (#6221, @ichekrygin)
+- ElasticJobs: Fix the bug that scheduling of the Pending workloads was not triggered on scale-down of the running 
+  elastic Job which could result in admitting one or more of the queued workloads. (#6395, @ichekrygin)
+- ElasticJobs: workloads correctly trigger workload preemption in response to a scale-up event. (#6973, @ichekrygin)
+- FS: Fix the algorithm bug for identifying preemption candidates, as it could return a different
+  set of preemption target workloads (pseudo random) in consecutive attempts in tie-break scenarios,
+  resulting in excessive preemptions. (#6764, @PBundyra)
+- FS: Fix the following FairSharing bugs:
+  - Incorrect DominantResourceShare caused by rounding (large quotas or high FairSharing weight)
+  - Preemption loop caused by zero FairSharing weight (#6925, @gabesaba)
+- FS: Fixing a bug where a preemptor ClusterQueue was unable to reclaim its nominal quota when the preemptee ClusterQueue can borrow a large number of resources from the parent ClusterQueue / Cohort (#6617, @pajakd)
+- FS: Validate FairSharing.Weight against small values which lose precision (0 < value <= 10^-9) (#6986, @gabesaba)
+- Fix accounting for the `evicted_workloads_once_total` metric:
+  - the metric wasn't incremented for workloads evicted due to stopped LocalQueue (LocalQueueStopped reason)
+  - the reason used for the metric was "Deactivated" for workloads deactivated by users and Kueue, now the reason label can have the following values: Deactivated, DeactivatedDueToAdmissionCheck, DeactivatedDueToMaximumExecutionTimeExceeded, DeactivatedDueToRequeuingLimitExceeded. This approach aligns the metric with `evicted_workloads_total`.
+  - the metric was incremented during preemption before the preemption request was issued. Thus, it could be incorrectly over-counted in case of the preemption request failure.
+  - the metric was not incremented for workload evicted due to NodeFailures (TAS)
+  
+  The existing and introduced DeactivatedDueToXYZ reason label values will be replaced by the single "Deactivated" reason label value and underlying_cause in the future release. (#6332, @mimowo)
+- Fix bug in workload usage removal simulation that results in inaccurate flavor assignment (#7077, @gabesaba)
+- Fix support for PodGroup integration used by external controllers, which determine the 
+  the target LocalQueue and the group size only later. In that case the hash would not be 
+  computed resulting in downstream issues for ProvisioningRequest.
+  
+  Now such an external controller can indicate the control over the PodGroup by adding
+  the `kueue.x-k8s.io/pod-suspending-parent` annotation, and later patch the Pods by setting
+  other metadata, like the kueue.x-k8s.io/queue-name label to initiate scheduling of the PodGroup. (#6286, @pawloch00)
+- Fix the bug for the StatefulSet integration which would occasionally cause a StatefulSet
+  to be stuck without workload after renaming the "queue-name" label. (#7028, @IrvingMg)
+- Fix the bug that a workload going repeatedly via the preemption and re-admission cycle would accumulate the
+  "Previously" prefix in the condition message, eg: "Previously: Previously: Previously: Preempted to accommodate a workload ...". (#6819, @amy)
+- Fix the bug which could occasionally cause workloads evicted by the built-in AdmissionChecks
+  (ProvisioningRequest and MultiKueue) to get stuck in the evicted state which didn't allow re-scheduling.
+  This could happen when the AdmissionCheck controller would trigger eviction by setting the
+  Admission check state to "Retry". (#6283, @mimowo)
+- Fix the validation messages when attempting to remove the queue-name label from a Deployment or StatefulSet. (#6715, @Panlq)
+- Fixed a bug that prevented adding the kueue- prefix to the secretName field in cert-manager manifests when installing Kueue using the Kustomize configuration. (#6318, @mbobrovskyi)
+- HC: When multiple borrowing flavors are available, prefer the flavor which
+  results in borrowing more locally (closer to the ClusterQueue, further from the root Cohort).
+  
+  This fixes the scenario where a flavor would be selected which required borrowing
+  from the root Cohort in one flavor, while in a second flavor, quota was
+  available from the nearest parent Cohort. (#7024, @gabesaba)
+- Helm: Fix a bug where the internal cert manager assumed that the helm installation name is 'kueue'. (#6869, @cmtly)
+- Helm: Fixed a bug preventing Kueue from starting after installing via Helm with a release name other than "kueue" (#6799, @mbobrovskyi)
+- Helm: Fixed bug where webhook configurations assumed a helm install name as "kueue". (#6918, @cmtly)
+- KueueViz: Fix CORS configuration for development environments (#6603, @yankay)
+- KueueViz: Fix a bug that only localhost is an executable domain. (#7011, @kincl)
+- Pod-integration now correctly handles pods stuck in the Terminating state within pod groups, preventing them from being counted as active and avoiding blocked quota release. (#6872, @ichekrygin)
+- ProvisioningRequest: Fix a bug that Kueue didn't recreate the next ProvisioningRequest instance after the
+  second (and consecutive) failed attempt. (#6322, @PBundyra)
+- Support disabling client-side ratelimiting in Config API clientConnection.qps with a negative value (e.g., -1) (#6300, @tenzen-y)
+- TAS: Fix a bug that the node failure controller tries to re-schedule Pods on the failure node even after the Node is recovered and reappears (#6325, @pajakd)
+- TAS: Fix a bug where new Workloads starve, caused by inadmissible workloads frequently requeueing due to unrelated Node LastHeartbeatTime update events. (#6570, @utam0k)
+- TAS: Fix the scenario when Node Hot Swap cannot find a replacement. In particular, if slices are used
+  they could result in generating invalid assignment, resulting in panic from TopologyUngater.
+  Now, such a workload is evicted. (#6914, @PBundyra)
+- TAS: Node Hot Swap allows replacing a node for workloads using PodSet slices, 
+  ie. when the `kueue.x-k8s.io/podset-slice-size` annotation is used. (#6942, @pajakd)
+- TAS: fix the bug that Kueue is crashing when PodSet has size 0, eg. no workers in LeaderWorkerSet instance. (#6501, @mimowo)
+
+### Other (Cleanup or Flake)
+
+- Promote ConfigurableResourceTransformations feature gate to stable. (#6599, @mbobrovskyi)
+- Support for Kubernetes 1.34 (#6689, @mbobrovskyi)
+- TAS: stop setting the "kueue.x-k8s.io/tas" label on Pods. 
+  
+  In case the implicit TAS mode is used, then the `kueue.x-k8s.io/podset-unconstrained-topology=true` annotation
+  is set on Pods. (#6895, @mimowo)
diff --git a/Dockerfile b/Dockerfile
index 36eb3ce44..4a865b09a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # defaulted ARGs need to be declared first
-ARG BUILDER_IMAGE=golang:1.24
+ARG BUILDER_IMAGE=golang:1.25
 ARG BASE_IMAGE=gcr.io/distroless/static:nonroot
 # compilation stage for the manager binary
 FROM --platform=${BUILDPLATFORM} ${BUILDER_IMAGE} AS builder
diff --git a/Makefile b/Makefile
index eca5e7c06..a41fd1604 100644
--- a/Makefile
+++ b/Makefile
@@ -87,7 +87,7 @@ LD_FLAGS += -X '$(version_pkg).BuildDate=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)'
 
 # Update these variables when preparing a new release or a release branch.
 # Then run `make prepare-release-branch`
-RELEASE_VERSION=v0.13.4
+RELEASE_VERSION=v0.14.0
 RELEASE_BRANCH=main
 # Application version for Helm and npm (strips leading 'v' from RELEASE_VERSION)
 APP_VERSION := $(shell echo $(RELEASE_VERSION) | cut -c2-)
diff --git a/README.md b/README.md
index 49290537b..a28d14399 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ Read the [overview](https://kueue.sigs.k8s.io/docs/overview/) and watch the Kueu
 To install the latest release of Kueue in your cluster, run the following command:
 
 ```shell
-kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.13.4/manifests.yaml
+kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.14.0/manifests.yaml
 ```
 
 The controller runs in the `kueue-system` namespace.
diff --git a/SECURITY-INSIGHTS.yaml b/SECURITY-INSIGHTS.yaml
index 0e8349aa3..acbf1cbf0 100644
--- a/SECURITY-INSIGHTS.yaml
+++ b/SECURITY-INSIGHTS.yaml
@@ -1,11 +1,11 @@
 header:
   schema-version: 1.0.0
   expiration-date: '2024-09-28T01:00:00.000Z'
-  last-updated: '2025-09-12'
-  last-reviewed: '2025-09-12'
-  commit-hash: "07ffcfc19fe9c83698a160df1b523b379f45db97"
+  last-updated: '2025-09-30'
+  last-reviewed: '2025-09-30'
+  commit-hash: "431c2e538b87319c838e3a993dfeec2b077f8fa2"
   project-url: 'https://github.com/kubernetes-sigs/kueue'
-  project-release: "0.13.4"
+  project-release: "0.14.0"
   changelog: 'https://github.com/kubernetes-sigs/kueue/tree/main/CHANGELOG'
   license: 'https://github.com/kubernetes-sigs/kueue/blob/main/LICENSE'
 project-lifecycle:
@@ -28,7 +28,7 @@ documentation:
   - 'https://kueue.sigs.k8s.io/docs/'
 distribution-points:
   - >-
-    https://github.com/kubernetes-sigs/kueue/releases/download/v0.13.4/manifests.yaml
+    https://github.com/kubernetes-sigs/kueue/releases/download/v0.14.0/manifests.yaml
 security-artifacts:
   threat-model:
     threat-model-created: false
@@ -62,5 +62,5 @@ dependencies:
   dependencies-lists:
     - 'https://github.com/kubernetes-sigs/kueue/blob/main/go.mod'
   sbom:
-    - sbom-file: https://github.com/kubernetes-sigs/kueue/releases/download/v0.13.4/kueue-v0.13.4.spdx.json
+    - sbom-file: https://github.com/kubernetes-sigs/kueue/releases/download/v0.14.0/kueue-v0.14.0.spdx.json
       sbom-format: SPDX
diff --git a/charts/kueue/Chart.yaml b/charts/kueue/Chart.yaml
index 31ed82945..5047319b4 100644
--- a/charts/kueue/Chart.yaml
+++ b/charts/kueue/Chart.yaml
@@ -16,9 +16,9 @@ type: application
 # NOTE: Do not modify manually. In Kueue, the version and appVersion are
 # overridden to GIT_TAG when building the artifacts, including the helm charts,
 # via Makefile.
-version: 0.13.4
+version: 0.14.0
 # This is the version number of the application being deployed. This version number should be
 # incremented each time you make changes to the application. Versions are not expected to
 # follow Semantic Versioning. They should reflect the version the application is using.
 # It is recommended to use it with quotes.
-appVersion: "v0.13.4"
+appVersion: "v0.14.0"
diff --git a/charts/kueue/README.md b/charts/kueue/README.md
index f6694fa3b..f943d14a2 100644
--- a/charts/kueue/README.md
+++ b/charts/kueue/README.md
@@ -1,6 +1,6 @@
 # kueue
 
-![Version: 0.13.4](https://img.shields.io/badge/Version-0.13.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.13.4](https://img.shields.io/badge/AppVersion-v0.13.4-informational?style=flat-square)
+![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.14.0](https://img.shields.io/badge/AppVersion-v0.14.0-informational?style=flat-square)
 
 Kueue is a set of APIs and controllers for job queueing. It is a job-level manager that decides when a job should be admitted to start (as in pods can be created) and when it should stop (as in active pods should be deleted).
 
@@ -28,7 +28,7 @@ $ helm install kueue kueue/ --create-namespace --namespace kueue-system
 Or use the charts pushed to `oci://registry.k8s.io/kueue/charts/kueue`:
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" --create-namespace --namespace=kueue-system
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" --create-namespace --namespace=kueue-system
 ```
 
 For more advanced parametrization of Kueue, we recommend using a local overrides file, passed via the `--values` flag. For example:
@@ -50,7 +50,7 @@ controllerManager:
 ```
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" \
   --create-namespace --namespace=kueue-system \
   --values overrides.yaml
 ```
@@ -58,7 +58,7 @@ helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
 You can also use the `--set` flag. For example, to enable a feature gate (e.g., `TopologyAwareScheduling`):
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" \
   --create-namespace --namespace=kueue-system \
   --set "controllerManager.featureGates[0].name=TopologyAwareScheduling" \
   --set "controllerManager.featureGates[0].enabled=true"
diff --git a/charts/kueue/README.md.gotmpl b/charts/kueue/README.md.gotmpl
index c3f139823..e5dda8bd2 100644
--- a/charts/kueue/README.md.gotmpl
+++ b/charts/kueue/README.md.gotmpl
@@ -30,7 +30,7 @@ $ helm install kueue kueue/ --create-namespace --namespace kueue-system
 Or use the charts pushed to `oci://registry.k8s.io/kueue/charts/kueue`:
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" --create-namespace --namespace=kueue-system
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" --create-namespace --namespace=kueue-system
 ```
 
 For more advanced parametrization of Kueue, we recommend using a local overrides file, passed via the `--values` flag. For example:
@@ -52,7 +52,7 @@ controllerManager:
 ```
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" \
   --create-namespace --namespace=kueue-system \
   --values overrides.yaml
 ```
@@ -60,7 +60,7 @@ helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
 You can also use the `--set` flag. For example, to enable a feature gate (e.g., `TopologyAwareScheduling`):
 
 ```bash
-helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.13.4" \
+helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version="0.14.0" \
   --create-namespace --namespace=kueue-system \
   --set "controllerManager.featureGates[0].name=TopologyAwareScheduling" \
   --set "controllerManager.featureGates[0].enabled=true"
diff --git a/charts/kueue/templates/webhook/manifests.yaml b/charts/kueue/templates/webhook/manifests.yaml
index d2ae17a24..784e03f67 100644
--- a/charts/kueue/templates/webhook/manifests.yaml
+++ b/charts/kueue/templates/webhook/manifests.yaml
@@ -242,26 +242,6 @@ webhooks:
           - xgboostjobs
     sideEffects: None
     reinvocationPolicy: '{{ .Values.mutatingWebhook.reinvocationPolicy }}'
-  - admissionReviewVersions:
-      - v1
-    clientConfig:
-      service:
-        name: '{{ include "kueue.fullname" . }}-webhook-service'
-        namespace: '{{ .Release.Namespace }}'
-        path: /mutate-trainer-kubeflow-org-v1alpha1-trainjob
-    failurePolicy: Fail
-    name: mtrainjob.kb.io
-    rules:
-      - apiGroups:
-          - trainer.kubeflow.org
-        apiVersions:
-          - v1alpha1
-        operations:
-          - CREATE
-        resources:
-          - trainjobs
-    sideEffects: None
-    reinvocationPolicy: '{{ .Values.mutatingWebhook.reinvocationPolicy }}'
   - admissionReviewVersions:
       - v1
     clientConfig:
@@ -430,6 +410,26 @@ webhooks:
           - statefulsets
     sideEffects: None
     reinvocationPolicy: '{{ .Values.mutatingWebhook.reinvocationPolicy }}'
+  - admissionReviewVersions:
+      - v1
+    clientConfig:
+      service:
+        name: '{{ include "kueue.fullname" . }}-webhook-service'
+        namespace: '{{ .Release.Namespace }}'
+        path: /mutate-trainer-kubeflow-org-v1alpha1-trainjob
+    failurePolicy: Fail
+    name: mtrainjob.kb.io
+    rules:
+      - apiGroups:
+          - trainer.kubeflow.org
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+        resources:
+          - trainjobs
+    sideEffects: None
+    reinvocationPolicy: '{{ .Values.mutatingWebhook.reinvocationPolicy }}'
   - admissionReviewVersions:
       - v1
     clientConfig:
@@ -730,26 +730,6 @@ webhooks:
         resources:
           - xgboostjobs
     sideEffects: None
-  - admissionReviewVersions:
-      - v1
-    clientConfig:
-      service:
-        name: '{{ include "kueue.fullname" . }}-webhook-service'
-        namespace: '{{ .Release.Namespace }}'
-        path: /validate-trainer-kubeflow-org-v1alpha1-trainjob
-    failurePolicy: Fail
-    name: vtrainjob.kb.io
-    rules:
-      - apiGroups:
-          - trainer.kubeflow.org
-        apiVersions:
-          - v1alpha1
-        operations:
-          - CREATE
-          - UPDATE
-        resources:
-          - trainjobs
-    sideEffects: None
   - admissionReviewVersions:
       - v1
     clientConfig:
@@ -916,6 +896,26 @@ webhooks:
         resources:
           - statefulsets
     sideEffects: None
+  - admissionReviewVersions:
+      - v1
+    clientConfig:
+      service:
+        name: '{{ include "kueue.fullname" . }}-webhook-service'
+        namespace: '{{ .Release.Namespace }}'
+        path: /validate-trainer-kubeflow-org-v1alpha1-trainjob
+    failurePolicy: Fail
+    name: vtrainjob.kb.io
+    rules:
+      - apiGroups:
+          - trainer.kubeflow.org
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - trainjobs
+    sideEffects: None
   - admissionReviewVersions:
       - v1
     clientConfig:
diff --git a/cmd/kueueviz/INSTALL.md b/cmd/kueueviz/INSTALL.md
index 6b6787ac0..500208a85 100644
--- a/cmd/kueueviz/INSTALL.md
+++ b/cmd/kueueviz/INSTALL.md
@@ -3,7 +3,7 @@
 KueueViz can be installed using `kubectl` with the following command:
 
 ```
-kubectl create -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.13.4/kueueviz.yaml
+kubectl create -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.14.0/kueueviz.yaml
 ```
 If you are using `kind` and that you don't have an `ingress` controller, you can use `port-forward` to 
 configure and run `KueueViz`:
@@ -23,7 +23,7 @@ by ensuring that `enableKueueViz` is set to `true`:
 
 ```
 helm upgrade --install kueue oci://registry.k8s.io/kueue/charts/kueue \
-  --version="0.13.4"
+  --version="0.14.0"
   --namespace kueue-system \
   --set enableKueueViz=true \
   --create-namespace
@@ -44,7 +44,7 @@ kind create cluster
 kind get kubeconfig > kubeconfig
 export KUBECONFIG=$PWD/kubeconfig
 helm install kueue oci://us-central1-docker.pkg.dev/k8s-staging-images/charts/kueue \
-            --version="0.13.4" --create-namespace --namespace=kueue-system
+            --version="0.14.0" --create-namespace --namespace=kueue-system
 ```
 
 ## Build
diff --git a/cmd/kueueviz/backend/go.mod b/cmd/kueueviz/backend/go.mod
index fc7b3fcb0..81dcc25f0 100644
--- a/cmd/kueueviz/backend/go.mod
+++ b/cmd/kueueviz/backend/go.mod
@@ -1,6 +1,6 @@
 module kueueviz
 
-go 1.24.0
+go 1.25.0
 
 require (
 	github.com/gin-contrib/cors v1.7.6
diff --git a/cmd/kueueviz/frontend/package-lock.json b/cmd/kueueviz/frontend/package-lock.json
index bc57fc689..ea78ada22 100644
--- a/cmd/kueueviz/frontend/package-lock.json
+++ b/cmd/kueueviz/frontend/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "kueueviz-frontend",
-  "version": "0.13.4",
+  "version": "0.14.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "kueueviz-frontend",
-      "version": "0.13.4",
+      "version": "0.14.0",
       "license": "Apache 2.0",
       "dependencies": {
         "@emotion/react": "^11.14.0",
diff --git a/cmd/kueueviz/frontend/package.json b/cmd/kueueviz/frontend/package.json
index 872efd84b..da9f0add1 100644
--- a/cmd/kueueviz/frontend/package.json
+++ b/cmd/kueueviz/frontend/package.json
@@ -1,6 +1,6 @@
 {
   "name": "kueueviz-frontend",
-  "version": "0.13.4",
+  "version": "0.14.0",
   "private": true,
   "description": "Frontend dashboard for visualizing Kueue status",
   "main": "src/index.jsx",
diff --git a/config/components/webhook/manifests.yaml b/config/components/webhook/manifests.yaml
index ca27ba224..26caf441f 100644
--- a/config/components/webhook/manifests.yaml
+++ b/config/components/webhook/manifests.yaml
@@ -176,25 +176,6 @@ webhooks:
     resources:
     - xgboostjobs
   sideEffects: None
-- admissionReviewVersions:
-  - v1
-  clientConfig:
-    service:
-      name: webhook-service
-      namespace: system
-      path: /mutate-trainer-kubeflow-org-v1alpha1-trainjob
-  failurePolicy: Fail
-  name: mtrainjob.kb.io
-  rules:
-  - apiGroups:
-    - trainer.kubeflow.org
-    apiVersions:
-    - v1alpha1
-    operations:
-    - CREATE
-    resources:
-    - trainjobs
-  sideEffects: None
 - admissionReviewVersions:
   - v1
   clientConfig:
@@ -311,6 +292,25 @@ webhooks:
     resources:
     - statefulsets
   sideEffects: None
+- admissionReviewVersions:
+  - v1
+  clientConfig:
+    service:
+      name: webhook-service
+      namespace: system
+      path: /mutate-trainer-kubeflow-org-v1alpha1-trainjob
+  failurePolicy: Fail
+  name: mtrainjob.kb.io
+  rules:
+  - apiGroups:
+    - trainer.kubeflow.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    resources:
+    - trainjobs
+  sideEffects: None
 - admissionReviewVersions:
   - v1
   clientConfig:
@@ -554,26 +554,6 @@ webhooks:
     resources:
     - xgboostjobs
   sideEffects: None
-- admissionReviewVersions:
-  - v1
-  clientConfig:
-    service:
-      name: webhook-service
-      namespace: system
-      path: /validate-trainer-kubeflow-org-v1alpha1-trainjob
-  failurePolicy: Fail
-  name: vtrainjob.kb.io
-  rules:
-  - apiGroups:
-    - trainer.kubeflow.org
-    apiVersions:
-    - v1alpha1
-    operations:
-    - CREATE
-    - UPDATE
-    resources:
-    - trainjobs
-  sideEffects: None
 - admissionReviewVersions:
   - v1
   clientConfig:
@@ -694,6 +674,26 @@ webhooks:
     resources:
     - statefulsets
   sideEffects: None
+- admissionReviewVersions:
+  - v1
+  clientConfig:
+    service:
+      name: webhook-service
+      namespace: system
+      path: /validate-trainer-kubeflow-org-v1alpha1-trainjob
+  failurePolicy: Fail
+  name: vtrainjob.kb.io
+  rules:
+  - apiGroups:
+    - trainer.kubeflow.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    - UPDATE
+    resources:
+    - trainjobs
+  sideEffects: None
 - admissionReviewVersions:
   - v1
   clientConfig:
diff --git a/go.mod b/go.mod
index 6ea247f76..04d809c95 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module sigs.k8s.io/kueue
 
-go 1.24.0
+go 1.25.0
 
 require (
 	github.com/cert-manager/cert-manager v1.18.2
@@ -38,7 +38,7 @@ require (
 	k8s.io/metrics v0.34.1
 	k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
 	sigs.k8s.io/controller-runtime v0.22.1
-	sigs.k8s.io/jobset v0.9.0
+	sigs.k8s.io/jobset v0.10.1
 	sigs.k8s.io/lws v0.7.0
 	sigs.k8s.io/structured-merge-diff/v6 v6.3.0
 	sigs.k8s.io/yaml v1.6.0
diff --git a/go.sum b/go.sum
index 018ca7c42..60cb494f1 100644
--- a/go.sum
+++ b/go.sum
@@ -392,8 +392,8 @@ sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV
 sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
 sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM=
 sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs=
-sigs.k8s.io/jobset v0.9.0 h1:B8IBKEy25w6Cu46+eFBbWtuXnI9goLTz6UY6fa4qSoA=
-sigs.k8s.io/jobset v0.9.0/go.mod h1:ysr9t5IxXIZEXgf4JACLJQ1UE2GOeUUzfsP+tnIG/OQ=
+sigs.k8s.io/jobset v0.10.1 h1:u8QKifNsrWVrlRFe6w2ofYTqFT9ma2DRHxfeB2m9xyU=
+sigs.k8s.io/jobset v0.10.1/go.mod h1:B9jF+ttb/qAs6LyJ036QTJ0LwvVnY/ax1fIKAqn4AYE=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
 sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
diff --git a/hack/cypress/Dockerfile b/hack/cypress/Dockerfile
index a754ecafe..682d1a2de 100644
--- a/hack/cypress/Dockerfile
+++ b/hack/cypress/Dockerfile
@@ -1 +1 @@
-FROM cypress/base:22.19.0
+FROM cypress/base:22.20.0
diff --git a/hack/internal/tools/go.mod b/hack/internal/tools/go.mod
index eae968f48..bc32eb66f 100644
--- a/hack/internal/tools/go.mod
+++ b/hack/internal/tools/go.mod
@@ -1,6 +1,6 @@
 module sigs.k8s.io/kueue/internal/tools
 
-go 1.24.0
+go 1.25.0
 
 tool (
 	github.com/gohugoio/hugo
diff --git a/hack/releasing/sync-notes.sh b/hack/releasing/sync-notes.sh
index 93b3e6a74..bafd2a997 100755
--- a/hack/releasing/sync-notes.sh
+++ b/hack/releasing/sync-notes.sh
@@ -151,7 +151,7 @@ fi
 RELEASE_ISSUE_NAME="Release ${RELEASE_VERSION}"
 declare RELEASE_ISSUE_NAME
 
-RELEASE_ISSUE_NUMBER=$(gh issue list --repo="${MAIN_REPO_ORG}/${MAIN_REPO_NAME}" | grep "${RELEASE_ISSUE_NAME}" | awk '{print $1}' || true)
+RELEASE_ISSUE_NUMBER=$(gh issue list --repo="${MAIN_REPO_ORG}/${MAIN_REPO_NAME}" --search "in:title ${RELEASE_ISSUE_NAME}" | awk '{print $1}' || true)
 if [ -z "$RELEASE_ISSUE_NUMBER" ]; then
   echo "!!! No release issue found for version ${RELEASE_VERSION}. Please create 'Release ${RELEASE_VERSION}' issue first."
   exit 1
diff --git a/pkg/cache/scheduler/clusterqueue_snapshot.go b/pkg/cache/scheduler/clusterqueue_snapshot.go
index 9036df94a..8c132c6bb 100644
--- a/pkg/cache/scheduler/clusterqueue_snapshot.go
+++ b/pkg/cache/scheduler/clusterqueue_snapshot.go
@@ -72,24 +72,6 @@ func (c *ClusterQueueSnapshot) RGByResource(resource corev1.ResourceName) *Resou
 	return nil
 }
 
-// SimulateWorkloadRemoval modifies the snapshot by removing the usage
-// corresponding to the list of workloads. It returns a function which
-// can be used to restore the usage.
-func (c *ClusterQueueSnapshot) SimulateWorkloadRemoval(workloads []*workload.Info) func() {
-	usage := make([]workload.Usage, 0, len(workloads))
-	for _, w := range workloads {
-		usage = append(usage, w.Usage())
-	}
-	for _, u := range usage {
-		c.RemoveUsage(u)
-	}
-	return func() {
-		for _, u := range usage {
-			c.AddUsage(u)
-		}
-	}
-}
-
 // SimulateUsageAddition modifies the snapshot by adding usage, and
 // returns a function used to restore the usage.
 func (c *ClusterQueueSnapshot) SimulateUsageAddition(usage workload.Usage) func() {
diff --git a/pkg/cache/scheduler/snapshot.go b/pkg/cache/scheduler/snapshot.go
index f0968ec2e..8aa0af873 100644
--- a/pkg/cache/scheduler/snapshot.go
+++ b/pkg/cache/scheduler/snapshot.go
@@ -52,7 +52,7 @@ func (s *Snapshot) RemoveWorkload(wl *workload.Info) {
 	cq.RemoveUsage(wl.Usage())
 }
 
-// AddWorkload adds a workload from its corresponding ClusterQueue and
+// AddWorkload adds a workload to its corresponding ClusterQueue and
 // updates resource usage.
 func (s *Snapshot) AddWorkload(wl *workload.Info) {
 	cq := s.ClusterQueue(wl.ClusterQueue)
@@ -60,6 +60,29 @@ func (s *Snapshot) AddWorkload(wl *workload.Info) {
 	cq.AddUsage(wl.Usage())
 }
 
+// SimulateWorkloadRemoval modifies the snapshot by removing the usage
+// corresponding to the list of workloads from workloads' respective
+// ClusterQueues. It returns a function which can be used to restore
+// this usage.
+func (s *Snapshot) SimulateWorkloadRemoval(workloads []*workload.Info) func() {
+	type cqUsage struct {
+		cq    kueue.ClusterQueueReference
+		usage workload.Usage
+	}
+	cqUsages := make([]cqUsage, 0, len(workloads))
+	for _, w := range workloads {
+		cqUsages = append(cqUsages, cqUsage{cq: w.ClusterQueue, usage: w.Usage()})
+	}
+	for _, cqUsage := range cqUsages {
+		s.ClusterQueue(cqUsage.cq).RemoveUsage(cqUsage.usage)
+	}
+	return func() {
+		for _, cqUsage := range cqUsages {
+			s.ClusterQueue(cqUsage.cq).AddUsage(cqUsage.usage)
+		}
+	}
+}
+
 func (s *Snapshot) Log(log logr.Logger) {
 	for name, cq := range s.ClusterQueues() {
 		cohortName := "<none>"
diff --git a/pkg/controller/jobframework/tas_validation.go b/pkg/controller/jobframework/tas_validation.go
index 81d41d10e..62ecce85d 100644
--- a/pkg/controller/jobframework/tas_validation.go
+++ b/pkg/controller/jobframework/tas_validation.go
@@ -34,7 +34,6 @@ func ValidateTASPodSetRequest(replicaPath *field.Path, replicaMetadata *metav1.O
 	_, unconstrainedFound := replicaMetadata.Annotations[kueuebeta.PodSetUnconstrainedTopologyAnnotation]
 	sliceRequiredValue, sliceRequiredFound := replicaMetadata.Annotations[kueuebeta.PodSetSliceRequiredTopologyAnnotation]
 	_, sliceSizeFound := replicaMetadata.Annotations[kueuebeta.PodSetSliceSizeAnnotation]
-	podSetGroupNameValue, podSetGroupNameFound := replicaMetadata.Annotations[kueuebeta.PodSetGroupName]
 
 	// validate no more than 1 annotation
 	asInt := func(b bool) int {
@@ -66,8 +65,17 @@ func ValidateTASPodSetRequest(replicaPath *field.Path, replicaMetadata *metav1.O
 	}
 
 	// validate PodSetGroupName annotation
+	podSetGroupNameValue, podSetGroupNameFound := replicaMetadata.Annotations[kueuebeta.PodSetGroupName]
 	if podSetGroupNameFound {
 		allErrs = append(allErrs, validatePodSetGroupNameAnnotation(podSetGroupNameValue, annotationsPath.Key(kueuebeta.PodSetGroupName))...)
+
+		if sliceSizeFound {
+			allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetGroupName), fmt.Sprintf("may not be set when '%s' is specified", kueuebeta.PodSetSliceSizeAnnotation)))
+		}
+
+		if sliceRequiredFound {
+			allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetGroupName), fmt.Sprintf("may not be set when '%s' is specified", kueuebeta.PodSetSliceRequiredTopologyAnnotation)))
+		}
 	}
 
 	unconstrainedErrs := validateTASUnconstrained(annotationsPath, replicaMetadata)
@@ -78,19 +86,10 @@ func ValidateTASPodSetRequest(replicaPath *field.Path, replicaMetadata *metav1.O
 
 	// validate slice annotations
 	if sliceRequiredFound && !sliceSizeFound {
-		allErrs = append(allErrs, field.Required(annotationsPath.Key(kueuebeta.PodSetSliceSizeAnnotation), "slice size is required if slice topology is requested"))
+		allErrs = append(allErrs, field.Required(annotationsPath.Key(kueuebeta.PodSetSliceSizeAnnotation), fmt.Sprintf("must be set when '%s' is specified", kueuebeta.PodSetSliceRequiredTopologyAnnotation)))
 	}
 	if !sliceRequiredFound && sliceSizeFound {
-		allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetSliceSizeAnnotation), fmt.Sprintf("cannot be set when '%s' is not present", kueuebeta.PodSetSliceRequiredTopologyAnnotation)))
-	}
-	if podSetGroupNameFound {
-		if sliceSizeFound {
-			allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetSliceSizeAnnotation), fmt.Sprintf("cannot be set when '%s' is present", kueuebeta.PodSetGroupName)))
-		}
-
-		if sliceRequiredFound {
-			allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetSliceRequiredTopologyAnnotation), fmt.Sprintf("cannot be set when '%s' is present", kueuebeta.PodSetGroupName)))
-		}
+		allErrs = append(allErrs, field.Forbidden(annotationsPath.Key(kueuebeta.PodSetSliceSizeAnnotation), fmt.Sprintf("may not be set when '%s' is not specified", kueuebeta.PodSetSliceRequiredTopologyAnnotation)))
 	}
 
 	return allErrs
diff --git a/pkg/controller/jobs/jobs.go b/pkg/controller/jobs/jobs.go
index f9c38fca5..e3ca17b66 100644
--- a/pkg/controller/jobs/jobs.go
+++ b/pkg/controller/jobs/jobs.go
@@ -23,11 +23,11 @@ import (
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs"
-	_ "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/leaderworkerset"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/raycluster"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/rayjob"
 	_ "sigs.k8s.io/kueue/pkg/controller/jobs/statefulset"
+	_ "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 )
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_controller.go b/pkg/controller/jobs/trainjob/trainjob_controller.go
similarity index 90%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_controller.go
rename to pkg/controller/jobs/trainjob/trainjob_controller.go
index 87ee6438c..0d36ec091 100644
--- a/pkg/controller/jobs/kubeflow/trainjob/trainjob_controller.go
+++ b/pkg/controller/jobs/trainjob/trainjob_controller.go
@@ -23,7 +23,7 @@ import (
 	"fmt"
 	"strconv"
 
-	kftrainerapi "github.com/kubeflow/trainer/v2/pkg/apis/trainer/v1alpha1"
+	kftrainer "github.com/kubeflow/trainer/v2/pkg/apis/trainer/v1alpha1"
 	kftrainerruntime "github.com/kubeflow/trainer/v2/pkg/runtime"
 	kftrainerruntimecore "github.com/kubeflow/trainer/v2/pkg/runtime/core"
 	kftrainerjobset "github.com/kubeflow/trainer/v2/pkg/runtime/framework/plugins/jobset"
@@ -42,7 +42,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
-	kJobset "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
+	workloadjobset "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/podset"
 	clientutil "sigs.k8s.io/kueue/pkg/util/client"
@@ -50,7 +50,7 @@ import (
 )
 
 var (
-	gvk                    = kftrainerapi.GroupVersion.WithKind("TrainJob")
+	gvk                    = kftrainer.GroupVersion.WithKind("TrainJob")
 	FrameworkName          = "trainer.kubeflow.org/trainjob"
 	TrainJobControllerName = "trainer.kubeflow.org/trainjob-controller"
 )
@@ -66,8 +66,8 @@ func init() {
 		NewJob:            NewJob,
 		NewReconciler:     NewReconciler,
 		SetupWebhook:      SetupTrainJobWebhook,
-		JobType:           &kftrainerapi.TrainJob{},
-		AddToScheme:       kftrainerapi.AddToScheme,
+		JobType:           &kftrainer.TrainJob{},
+		AddToScheme:       kftrainer.AddToScheme,
 		MultiKueueAdapter: &multiKueueAdapter{},
 	}))
 }
@@ -108,11 +108,11 @@ func (r *trainJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
 
 func (r *trainJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	b := ctrl.NewControllerManagedBy(mgr).
-		For(&kftrainerapi.TrainJob{}).Owns(&kueue.Workload{}).Owns(&jobsetapi.JobSet{})
+		For(&kftrainer.TrainJob{}).Owns(&kueue.Workload{}).Owns(&jobsetapi.JobSet{})
 	return b.Complete(r)
 }
 
-type TrainJob kftrainerapi.TrainJob
+type TrainJob kftrainer.TrainJob
 
 var _ jobframework.GenericJob = (*TrainJob)(nil)
 var _ jobframework.JobWithCustomStop = (*TrainJob)(nil)
@@ -124,11 +124,11 @@ func NewJob() jobframework.GenericJob {
 }
 
 func fromObject(obj runtime.Object) *TrainJob {
-	return (*TrainJob)(obj.(*kftrainerapi.TrainJob))
+	return (*TrainJob)(obj.(*kftrainer.TrainJob))
 }
 
 func (t *TrainJob) Object() client.Object {
-	return (*kftrainerapi.TrainJob)(t)
+	return (*kftrainer.TrainJob)(t)
 }
 
 func (t *TrainJob) IsSuspended() bool {
@@ -167,7 +167,7 @@ func getChildJobSet(t *TrainJob) (*jobsetapi.JobSet, error) {
 		return nil, fmt.Errorf("unsupported runtime: %s", runtimeRefGK)
 	}
 
-	trainJob := (*kftrainerapi.TrainJob)(t)
+	trainJob := (*kftrainer.TrainJob)(t)
 	trSpec, err := getRuntimeSpec(trainJob)
 	if err != nil {
 		return nil, fmt.Errorf("runtime '%s' not found", trainJob.Spec.RuntimeRef.Name)
@@ -212,16 +212,16 @@ func jobsetApplyToJobset(jobsetApply *jobsetapplyapi.JobSetApplyConfiguration) (
 	return jobset, nil
 }
 
-func getRuntimeSpec(trainJob *kftrainerapi.TrainJob) (*kftrainerapi.TrainingRuntimeSpec, error) {
-	if *trainJob.Spec.RuntimeRef.Kind == kftrainerapi.ClusterTrainingRuntimeKind {
-		var ctr kftrainerapi.ClusterTrainingRuntime
+func getRuntimeSpec(trainJob *kftrainer.TrainJob) (*kftrainer.TrainingRuntimeSpec, error) {
+	if *trainJob.Spec.RuntimeRef.Kind == kftrainer.ClusterTrainingRuntimeKind {
+		var ctr kftrainer.ClusterTrainingRuntime
 		err := reconciler.client.Get(reconciler.ctx, client.ObjectKey{Name: trainJob.Spec.RuntimeRef.Name}, &ctr)
 		if err != nil {
 			return nil, err
 		}
 		return &ctr.Spec, nil
 	} else {
-		var tr kftrainerapi.TrainingRuntime
+		var tr kftrainer.TrainingRuntime
 		err := reconciler.client.Get(reconciler.ctx, client.ObjectKey{Namespace: trainJob.Namespace, Name: trainJob.Spec.RuntimeRef.Name}, &tr)
 		if err != nil {
 			return nil, err
@@ -235,7 +235,7 @@ func (t *TrainJob) PodSets() ([]kueue.PodSet, error) {
 	if err != nil {
 		return nil, err
 	}
-	return (*kJobset.JobSet)(jobset).PodSets()
+	return (*workloadjobset.JobSet)(jobset).PodSets()
 }
 
 func (t *TrainJob) RunWithPodSetsInfo(podSetsInfo []podset.PodSetInfo) error {
@@ -249,7 +249,7 @@ func (t *TrainJob) RunWithPodSetsInfo(podSetsInfo []podset.PodSetInfo) error {
 	}
 
 	if t.Spec.PodSpecOverrides == nil {
-		t.Spec.PodSpecOverrides = []kftrainerapi.PodSpecOverride{}
+		t.Spec.PodSpecOverrides = []kftrainer.PodSpecOverride{}
 	}
 	if t.Annotations == nil {
 		t.Annotations = map[string]string{}
@@ -257,8 +257,8 @@ func (t *TrainJob) RunWithPodSetsInfo(podSetsInfo []podset.PodSetInfo) error {
 	t.Annotations[firstOverrideIdx] = strconv.Itoa(len(t.Spec.PodSpecOverrides))
 	for _, info := range podSetsInfo {
 		// The trainjob controller merges each podSpecOverride sequentially, so any existing user provided override will be processed first
-		t.Spec.PodSpecOverrides = append(t.Spec.PodSpecOverrides, kftrainerapi.PodSpecOverride{
-			TargetJobs: []kftrainerapi.PodSpecOverrideTargetJob{
+		t.Spec.PodSpecOverrides = append(t.Spec.PodSpecOverrides, kftrainer.PodSpecOverride{
+			TargetJobs: []kftrainer.PodSpecOverrideTargetJob{
 				{Name: string(info.Name)},
 			},
 			// TODO: Set the labels/annotations when supported. See https://github.com/kubeflow/trainer/pull/2785
@@ -320,10 +320,10 @@ func (t *TrainJob) RestorePodSetsInfo(_ []podset.PodSetInfo) bool {
 }
 
 func (t *TrainJob) Finished() (message string, success, finished bool) {
-	if c := apimeta.FindStatusCondition(t.Status.Conditions, kftrainerapi.TrainJobComplete); c != nil && c.Status == metav1.ConditionTrue {
+	if c := apimeta.FindStatusCondition(t.Status.Conditions, kftrainer.TrainJobComplete); c != nil && c.Status == metav1.ConditionTrue {
 		return c.Message, true, true
 	}
-	if c := apimeta.FindStatusCondition(t.Status.Conditions, kftrainerapi.TrainJobFailed); c != nil && c.Status == metav1.ConditionTrue {
+	if c := apimeta.FindStatusCondition(t.Status.Conditions, kftrainer.TrainJobFailed); c != nil && c.Status == metav1.ConditionTrue {
 		return c.Message, false, true
 	}
 	return message, success, false
@@ -356,7 +356,7 @@ func (t *TrainJob) ReclaimablePods() ([]kueue.ReclaimablePod, error) {
 	}
 
 	ret := make([]kueue.ReclaimablePod, 0, len(jobset.Spec.ReplicatedJobs))
-	statuses := slices.ToRefMap(t.Status.JobsStatus, func(js *kftrainerapi.JobStatus) string { return js.Name })
+	statuses := slices.ToRefMap(t.Status.JobsStatus, func(js *kftrainer.JobStatus) string { return js.Name })
 
 	for i := range jobset.Spec.ReplicatedJobs {
 		spec := &jobset.Spec.ReplicatedJobs[i]
@@ -364,7 +364,7 @@ func (t *TrainJob) ReclaimablePods() ([]kueue.ReclaimablePod, error) {
 			if status.Succeeded > 0 && status.Succeeded <= spec.Replicas {
 				ret = append(ret, kueue.ReclaimablePod{
 					Name:  kueue.NewPodSetReference(spec.Name),
-					Count: status.Succeeded * kJobset.PodsCountPerReplica(spec),
+					Count: status.Succeeded * workloadjobset.PodsCountPerReplica(spec),
 				})
 			}
 		}
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_multikueue_adapter.go b/pkg/controller/jobs/trainjob/trainjob_multikueue_adapter.go
similarity index 100%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_multikueue_adapter.go
rename to pkg/controller/jobs/trainjob/trainjob_multikueue_adapter.go
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_webhook.go b/pkg/controller/jobs/trainjob/trainjob_webhook.go
similarity index 100%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_webhook.go
rename to pkg/controller/jobs/trainjob/trainjob_webhook.go
diff --git a/pkg/scheduler/preemption/preemption_oracle.go b/pkg/scheduler/preemption/preemption_oracle.go
index 084b030ce..0524409f1 100644
--- a/pkg/scheduler/preemption/preemption_oracle.go
+++ b/pkg/scheduler/preemption/preemption_oracle.go
@@ -57,7 +57,7 @@ func (p *PreemptionOracle) SimulatePreemption(log logr.Logger, cq *schdcache.Clu
 	for i, c := range candidates {
 		workloadsToPreempt[i] = c.WorkloadInfo
 	}
-	revertRemoval := cq.SimulateWorkloadRemoval(workloadsToPreempt)
+	revertRemoval := p.snapshot.SimulateWorkloadRemoval(workloadsToPreempt)
 	borrowAfterPreemptions, _ := classical.FindHeightOfLowestSubtreeThatFits(cq, fr, quantity)
 	revertRemoval()
 
diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go
index 12e463340..a9909d4da 100644
--- a/pkg/scheduler/scheduler.go
+++ b/pkg/scheduler/scheduler.go
@@ -280,7 +280,7 @@ func (s *Scheduler) schedule(ctx context.Context) wait.SpeedSignal {
 		}
 
 		usage := e.assignmentUsage()
-		if !fits(cq, &usage, preemptedWorkloads, e.preemptionTargets) {
+		if !fits(snapshot, cq, &usage, preemptedWorkloads, e.preemptionTargets) {
 			setSkipped(e, "Workload no longer fits after processing another workload")
 			if mode == flavorassigner.Preempt {
 				skippedPreemptions[cq.Name]++
@@ -451,12 +451,12 @@ func (s *Scheduler) nominate(ctx context.Context, workloads []workload.Info, sna
 	return entries, inadmissibleEntries
 }
 
-func fits(cq *schdcache.ClusterQueueSnapshot, usage *workload.Usage, preemptedWorkloads preemption.PreemptedWorkloads, newTargets []*preemption.Target) bool {
+func fits(snapshot *schdcache.Snapshot, cq *schdcache.ClusterQueueSnapshot, usage *workload.Usage, preemptedWorkloads preemption.PreemptedWorkloads, newTargets []*preemption.Target) bool {
 	workloads := slices.Collect(maps.Values(preemptedWorkloads))
 	for _, target := range newTargets {
 		workloads = append(workloads, target.WorkloadInfo)
 	}
-	revertUsage := cq.SimulateWorkloadRemoval(workloads)
+	revertUsage := snapshot.SimulateWorkloadRemoval(workloads)
 	defer revertUsage()
 	return cq.Fits(*usage)
 }
@@ -506,7 +506,7 @@ type partialAssignment struct {
 func (s *Scheduler) getAssignments(log logr.Logger, wl *workload.Info, snap *schdcache.Snapshot) (flavorassigner.Assignment, []*preemption.Target) {
 	assignment, targets := s.getInitialAssignments(log, wl, snap)
 	cq := snap.ClusterQueue(wl.ClusterQueue)
-	updateAssignmentForTAS(cq, wl, &assignment, targets)
+	updateAssignmentForTAS(snap, cq, wl, &assignment, targets)
 	return assignment, targets
 }
 
@@ -584,7 +584,7 @@ func (s *Scheduler) evictWorkloadAfterFailedTASReplacement(ctx context.Context,
 	return nil
 }
 
-func updateAssignmentForTAS(cq *schdcache.ClusterQueueSnapshot, wl *workload.Info, assignment *flavorassigner.Assignment, targets []*preemption.Target) {
+func updateAssignmentForTAS(snapshot *schdcache.Snapshot, cq *schdcache.ClusterQueueSnapshot, wl *workload.Info, assignment *flavorassigner.Assignment, targets []*preemption.Target) {
 	if features.Enabled(features.TopologyAwareScheduling) && assignment.RepresentativeMode() == flavorassigner.Preempt &&
 		(workload.IsExplicitlyRequestingTAS(wl.Obj.Spec.PodSets...) || cq.IsTASOnly()) && !workload.HasTopologyAssignmentWithUnhealthyNode(wl.Obj) {
 		tasRequests := assignment.WorkloadsTopologyRequests(wl, cq)
@@ -594,7 +594,7 @@ func updateAssignmentForTAS(cq *schdcache.ClusterQueueSnapshot, wl *workload.Inf
 			for _, target := range targets {
 				targetWorkloads = append(targetWorkloads, target.WorkloadInfo)
 			}
-			revertUsage := cq.SimulateWorkloadRemoval(targetWorkloads)
+			revertUsage := snapshot.SimulateWorkloadRemoval(targetWorkloads)
 			tasResult = cq.FindTopologyAssignmentsForWorkload(tasRequests)
 			revertUsage()
 		} else {
diff --git a/site/content/en/docs/reference/kueue-alpha.v1alpha1.md b/site/content/en/docs/reference/kueue-alpha.v1alpha1.md
deleted file mode 100644
index 1dc6e23ab..000000000
--- a/site/content/en/docs/reference/kueue-alpha.v1alpha1.md
+++ /dev/null
@@ -1,100 +0,0 @@
----
-title: Kueue Alpha API
-content_type: tool-reference
-package: kueue.x-k8s.io/v1alpha1
-auto_generated: true
-description: Generated API reference documentation for kueue.x-k8s.io/v1alpha1.
----
-
-
-## Resource Types 
-
-
-- [Topology](#kueue-x-k8s-io-v1alpha1-Topology)
-  
-
-## `Topology`     {#kueue-x-k8s-io-v1alpha1-Topology}
-    
-
-**Appears in:**
-
-
-
-<p>Topology is the Schema for the topology API</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-<tr><td><code>apiVersion</code><br/>string</td><td><code>kueue.x-k8s.io/v1alpha1</code></td></tr>
-<tr><td><code>kind</code><br/>string</td><td><code>Topology</code></td></tr>
-    
-  
-<tr><td><code>spec</code> <B>[Required]</B><br/>
-<a href="#kueue-x-k8s-io-v1alpha1-TopologySpec"><code>TopologySpec</code></a>
-</td>
-<td>
-   <span class="text-muted">No description provided.</span></td>
-</tr>
-</tbody>
-</table>
-
-## `TopologyLevel`     {#kueue-x-k8s-io-v1alpha1-TopologyLevel}
-    
-
-**Appears in:**
-
-- [TopologySpec](#kueue-x-k8s-io-v1alpha1-TopologySpec)
-
-
-<p>TopologyLevel defines the desired state of TopologyLevel</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-  
-<tr><td><code>nodeLabel</code> <B>[Required]</B><br/>
-<code>string</code>
-</td>
-<td>
-   <p>nodeLabel indicates the name of the node label for a specific topology
-level.</p>
-<p>Examples:</p>
-<ul>
-<li>cloud.provider.com/topology-block</li>
-<li>cloud.provider.com/topology-rack</li>
-</ul>
-</td>
-</tr>
-</tbody>
-</table>
-
-## `TopologySpec`     {#kueue-x-k8s-io-v1alpha1-TopologySpec}
-    
-
-**Appears in:**
-
-- [Topology](#kueue-x-k8s-io-v1alpha1-Topology)
-
-
-<p>TopologySpec defines the desired state of Topology</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-  
-<tr><td><code>levels</code> <B>[Required]</B><br/>
-<a href="#kueue-x-k8s-io-v1alpha1-TopologyLevel"><code>[]TopologyLevel</code></a>
-</td>
-<td>
-   <p>levels define the levels of topology.</p>
-</td>
-</tr>
-</tbody>
-</table>
-  
\ No newline at end of file
diff --git a/site/content/zh-CN/docs/reference/kueue-alpha.v1alpha1.md b/site/content/zh-CN/docs/reference/kueue-alpha.v1alpha1.md
deleted file mode 100644
index 1dc6e23ab..000000000
--- a/site/content/zh-CN/docs/reference/kueue-alpha.v1alpha1.md
+++ /dev/null
@@ -1,100 +0,0 @@
----
-title: Kueue Alpha API
-content_type: tool-reference
-package: kueue.x-k8s.io/v1alpha1
-auto_generated: true
-description: Generated API reference documentation for kueue.x-k8s.io/v1alpha1.
----
-
-
-## Resource Types 
-
-
-- [Topology](#kueue-x-k8s-io-v1alpha1-Topology)
-  
-
-## `Topology`     {#kueue-x-k8s-io-v1alpha1-Topology}
-    
-
-**Appears in:**
-
-
-
-<p>Topology is the Schema for the topology API</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-<tr><td><code>apiVersion</code><br/>string</td><td><code>kueue.x-k8s.io/v1alpha1</code></td></tr>
-<tr><td><code>kind</code><br/>string</td><td><code>Topology</code></td></tr>
-    
-  
-<tr><td><code>spec</code> <B>[Required]</B><br/>
-<a href="#kueue-x-k8s-io-v1alpha1-TopologySpec"><code>TopologySpec</code></a>
-</td>
-<td>
-   <span class="text-muted">No description provided.</span></td>
-</tr>
-</tbody>
-</table>
-
-## `TopologyLevel`     {#kueue-x-k8s-io-v1alpha1-TopologyLevel}
-    
-
-**Appears in:**
-
-- [TopologySpec](#kueue-x-k8s-io-v1alpha1-TopologySpec)
-
-
-<p>TopologyLevel defines the desired state of TopologyLevel</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-  
-<tr><td><code>nodeLabel</code> <B>[Required]</B><br/>
-<code>string</code>
-</td>
-<td>
-   <p>nodeLabel indicates the name of the node label for a specific topology
-level.</p>
-<p>Examples:</p>
-<ul>
-<li>cloud.provider.com/topology-block</li>
-<li>cloud.provider.com/topology-rack</li>
-</ul>
-</td>
-</tr>
-</tbody>
-</table>
-
-## `TopologySpec`     {#kueue-x-k8s-io-v1alpha1-TopologySpec}
-    
-
-**Appears in:**
-
-- [Topology](#kueue-x-k8s-io-v1alpha1-Topology)
-
-
-<p>TopologySpec defines the desired state of Topology</p>
-
-
-<table class="table">
-<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
-<tbody>
-    
-  
-<tr><td><code>levels</code> <B>[Required]</B><br/>
-<a href="#kueue-x-k8s-io-v1alpha1-TopologyLevel"><code>[]TopologyLevel</code></a>
-</td>
-<td>
-   <p>levels define the levels of topology.</p>
-</td>
-</tr>
-</tbody>
-</table>
-  
\ No newline at end of file
diff --git a/site/go.mod b/site/go.mod
index fa341d783..cff84385d 100644
--- a/site/go.mod
+++ b/site/go.mod
@@ -1,5 +1,5 @@
 module sigs.k8s.io/kueue/site
 
-go 1.24
+go 1.25
 
 require github.com/google/docsy v0.11.0 // indirect
diff --git a/site/hugo.toml b/site/hugo.toml
index 4e6fa1fcc..bbc1bb3ef 100644
--- a/site/hugo.toml
+++ b/site/hugo.toml
@@ -99,10 +99,10 @@ ignoreFiles = []
   # The major.minor version tag for the version of the docs represented in this
   # branch of the repository. Used in the "version-banner" partial to display a
   # version number for this doc set.
-  version = "v0.13.4"
+  version = "v0.14.0"
 
   # Version of Kueue without the leading "v", as used for Helm charts.
-  chart_version = "0.13.4"
+  chart_version = "0.14.0"
 
   # Flag used in the "version-banner" partial to decide whether to display a
   # banner on every page indicating that this is an archived version of the docs.
diff --git a/site/static/examples/tas/sample-gpu-queues.yaml b/site/static/examples/tas/sample-gpu-queues.yaml
index e69efc128..c75fd6b74 100644
--- a/site/static/examples/tas/sample-gpu-queues.yaml
+++ b/site/static/examples/tas/sample-gpu-queues.yaml
@@ -1,4 +1,4 @@
-apiVersion: kueue.x-k8s.io/v1alpha1
+apiVersion: kueue.x-k8s.io/v1beta1
 kind: Topology
 metadata:
   name: "default"
diff --git a/site/static/examples/tas/sample-queues.yaml b/site/static/examples/tas/sample-queues.yaml
index 46d1594c6..6d1c01740 100644
--- a/site/static/examples/tas/sample-queues.yaml
+++ b/site/static/examples/tas/sample-queues.yaml
@@ -1,4 +1,4 @@
-apiVersion: kueue.x-k8s.io/v1alpha1
+apiVersion: kueue.x-k8s.io/v1beta1
 kind: Topology
 metadata:
   name: "default"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index bc5590dcc..87d799ecc 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1640,7 +1640,7 @@ sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics
 # sigs.k8s.io/gateway-api v1.1.0
 ## explicit; go 1.22.0
 sigs.k8s.io/gateway-api/apis/v1
-# sigs.k8s.io/jobset v0.9.0
+# sigs.k8s.io/jobset v0.10.1
 ## explicit; go 1.24.0
 sigs.k8s.io/jobset/api/jobset/v1alpha2
 sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2
diff --git a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/jobset_types.go b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/jobset_types.go
index 59ea1f805..d9f2df4c9 100644
--- a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/jobset_types.go
+++ b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/jobset_types.go
@@ -339,7 +339,10 @@ const (
 )
 
 // FailurePolicyRule defines a FailurePolicyAction to be executed if a child job
-// fails due to a reason listed in OnJobFailureReasons.
+// fails due to a reason listed in OnJobFailureReasons and a message pattern
+// listed in OnJobFailureMessagePatterns. The rule must match both the job
+// failure reason and the job failure message. The rules are evaluated in
+// order and the first matching rule is executed.
 type FailurePolicyRule struct {
 	// The name of the failure policy rule.
 	// The name is defaulted to 'failurePolicyRuleN' where N is the index of the failure policy rule.
@@ -348,13 +351,21 @@ type FailurePolicyRule struct {
 	// The action to take if the rule is matched.
 	// +kubebuilder:validation:Enum:=FailJobSet;RestartJobSet;RestartJobSetAndIgnoreMaxRestarts
 	Action FailurePolicyAction `json:"action"`
-	// The requirement on the job failure reasons. The requirement
-	// is satisfied if at least one reason matches the list.
-	// The rules are evaluated in order, and the first matching
-	// rule is executed.
-	// An empty list applies the rule to any job failure reason.
+	// The requirement on the job failure reasons. The requirement is satisfied
+	// if at least one reason matches the list. An empty list matches any job
+	// failure reason.
 	// +kubebuilder:validation:UniqueItems:true
 	OnJobFailureReasons []string `json:"onJobFailureReasons,omitempty"`
+	// The requirement on the job failure message. The requirement is satisfied
+	// if at least one pattern (regex) matches the job failure message. An
+	// empty list matches any job failure message.
+	// The syntax of the regular expressions accepted is the same general
+	// syntax used by Perl, Python, and other languages. More precisely, it is
+	// the syntax accepted by RE2 and described at https://golang.org/s/re2syntax,
+	// except for \C. For an overview of the syntax, see
+	// https://pkg.go.dev/regexp/syntax.
+	// +kubebuilder:validation:UniqueItems:true
+	OnJobFailureMessagePatterns []string `json:"onJobFailureMessagePatterns,omitempty"`
 	// TargetReplicatedJobs are the names of the replicated jobs the operator applies to.
 	// An empty list will apply to all replicatedJobs.
 	// +optional
diff --git a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/openapi_generated.go b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/openapi_generated.go
index ac4e904db..a098c8dd7 100644
--- a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/openapi_generated.go
+++ b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/openapi_generated.go
@@ -152,7 +152,7 @@ func schema_jobset_api_jobset_v1alpha2_FailurePolicyRule(ref common.ReferenceCal
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
 			SchemaProps: spec.SchemaProps{
-				Description: "FailurePolicyRule defines a FailurePolicyAction to be executed if a child job fails due to a reason listed in OnJobFailureReasons.",
+				Description: "FailurePolicyRule defines a FailurePolicyAction to be executed if a child job fails due to a reason listed in OnJobFailureReasons and a message pattern listed in OnJobFailureMessagePatterns. The rule must match both the job failure reason and the job failure message. The rules are evaluated in order and the first matching rule is executed.",
 				Type:        []string{"object"},
 				Properties: map[string]spec.Schema{
 					"name": {
@@ -173,7 +173,22 @@ func schema_jobset_api_jobset_v1alpha2_FailurePolicyRule(ref common.ReferenceCal
 					},
 					"onJobFailureReasons": {
 						SchemaProps: spec.SchemaProps{
-							Description: "The requirement on the job failure reasons. The requirement is satisfied if at least one reason matches the list. The rules are evaluated in order, and the first matching rule is executed. An empty list applies the rule to any job failure reason.",
+							Description: "The requirement on the job failure reasons. The requirement is satisfied if at least one reason matches the list. An empty list matches any job failure reason.",
+							Type:        []string{"array"},
+							Items: &spec.SchemaOrArray{
+								Schema: &spec.Schema{
+									SchemaProps: spec.SchemaProps{
+										Default: "",
+										Type:    []string{"string"},
+										Format:  "",
+									},
+								},
+							},
+						},
+					},
+					"onJobFailureMessagePatterns": {
+						SchemaProps: spec.SchemaProps{
+							Description: "The requirement on the job failure message. The requirement is satisfied if at least one pattern (regex) matches the job failure message. An empty list matches any job failure message. The syntax of the regular expressions accepted is the same general syntax used by Perl, Python, and other languages. More precisely, it is the syntax accepted by RE2 and described at https://golang.org/s/re2syntax, except for \\C. For an overview of the syntax, see https://pkg.go.dev/regexp/syntax.",
 							Type:        []string{"array"},
 							Items: &spec.SchemaOrArray{
 								Schema: &spec.Schema{
diff --git a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/zz_generated.deepcopy.go b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/zz_generated.deepcopy.go
index 4037434d3..229d685c5 100644
--- a/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/zz_generated.deepcopy.go
+++ b/vendor/sigs.k8s.io/jobset/api/jobset/v1alpha2/zz_generated.deepcopy.go
@@ -82,6 +82,11 @@ func (in *FailurePolicyRule) DeepCopyInto(out *FailurePolicyRule) {
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
+	if in.OnJobFailureMessagePatterns != nil {
+		in, out := &in.OnJobFailureMessagePatterns, &out.OnJobFailureMessagePatterns
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
 	if in.TargetReplicatedJobs != nil {
 		in, out := &in.TargetReplicatedJobs, &out.TargetReplicatedJobs
 		*out = make([]string, len(*in))
diff --git a/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/failurepolicyrule.go b/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/failurepolicyrule.go
index c16f9f8ea..005f0f999 100644
--- a/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/failurepolicyrule.go
+++ b/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/failurepolicyrule.go
@@ -21,10 +21,11 @@ import (
 // FailurePolicyRuleApplyConfiguration represents a declarative configuration of the FailurePolicyRule type for use
 // with apply.
 type FailurePolicyRuleApplyConfiguration struct {
-	Name                 *string                             `json:"name,omitempty"`
-	Action               *jobsetv1alpha2.FailurePolicyAction `json:"action,omitempty"`
-	OnJobFailureReasons  []string                            `json:"onJobFailureReasons,omitempty"`
-	TargetReplicatedJobs []string                            `json:"targetReplicatedJobs,omitempty"`
+	Name                        *string                             `json:"name,omitempty"`
+	Action                      *jobsetv1alpha2.FailurePolicyAction `json:"action,omitempty"`
+	OnJobFailureReasons         []string                            `json:"onJobFailureReasons,omitempty"`
+	OnJobFailureMessagePatterns []string                            `json:"onJobFailureMessagePatterns,omitempty"`
+	TargetReplicatedJobs        []string                            `json:"targetReplicatedJobs,omitempty"`
 }
 
 // FailurePolicyRuleApplyConfiguration constructs a declarative configuration of the FailurePolicyRule type for use with
@@ -59,6 +60,16 @@ func (b *FailurePolicyRuleApplyConfiguration) WithOnJobFailureReasons(values ...
 	return b
 }
 
+// WithOnJobFailureMessagePatterns adds the given value to the OnJobFailureMessagePatterns field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the OnJobFailureMessagePatterns field.
+func (b *FailurePolicyRuleApplyConfiguration) WithOnJobFailureMessagePatterns(values ...string) *FailurePolicyRuleApplyConfiguration {
+	for i := range values {
+		b.OnJobFailureMessagePatterns = append(b.OnJobFailureMessagePatterns, values[i])
+	}
+	return b
+}
+
 // WithTargetReplicatedJobs adds the given value to the TargetReplicatedJobs field in the declarative configuration
 // and returns the receiver, so that objects can be build by chaining "With" function invocations.
 // If called multiple times, values provided by each call will be appended to the TargetReplicatedJobs field.
diff --git a/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/jobset.go b/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/jobset.go
index 3f3cd7e1c..55ff1b51d 100644
--- a/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/jobset.go
+++ b/vendor/sigs.k8s.io/jobset/client-go/applyconfiguration/jobset/v1alpha2/jobset.go
@@ -39,6 +39,7 @@ func JobSet(name, namespace string) *JobSetApplyConfiguration {
 	b.WithAPIVersion("jobset.x-k8s.io/v1alpha2")
 	return b
 }
+func (b JobSetApplyConfiguration) IsApplyConfiguration() {}
 
 // WithKind sets the Kind field in the declarative configuration to the given value
 // and returns the receiver, so that objects can be built by chaining "With" function invocations.
@@ -214,8 +215,24 @@ func (b *JobSetApplyConfiguration) WithStatus(value *JobSetStatusApplyConfigurat
 	return b
 }
 
+// GetKind retrieves the value of the Kind field in the declarative configuration.
+func (b *JobSetApplyConfiguration) GetKind() *string {
+	return b.TypeMetaApplyConfiguration.Kind
+}
+
+// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration.
+func (b *JobSetApplyConfiguration) GetAPIVersion() *string {
+	return b.TypeMetaApplyConfiguration.APIVersion
+}
+
 // GetName retrieves the value of the Name field in the declarative configuration.
 func (b *JobSetApplyConfiguration) GetName() *string {
 	b.ensureObjectMetaApplyConfigurationExists()
 	return b.ObjectMetaApplyConfiguration.Name
 }
+
+// GetNamespace retrieves the value of the Namespace field in the declarative configuration.
+func (b *JobSetApplyConfiguration) GetNamespace() *string {
+	b.ensureObjectMetaApplyConfigurationExists()
+	return b.ObjectMetaApplyConfiguration.Namespace
+}



Test patch:
diff --git a/pkg/controller/jobs/job/job_webhook_test.go b/pkg/controller/jobs/job/job_webhook_test.go
index 150b41ffa..42d66b3cf 100644
--- a/pkg/controller/jobs/job/job_webhook_test.go
+++ b/pkg/controller/jobs/job/job_webhook_test.go
@@ -311,7 +311,7 @@ func TestValidateCreate(t *testing.T) {
 				PodAnnotation(kueue.PodSetSliceRequiredTopologyAnnotation, "cloud.com/block").
 				Obj(),
 			wantValidationErrs: field.ErrorList{
-				field.Required(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "slice size is required if slice topology is requested"),
+				field.Required(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "must be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 			},
 			topologyAwareScheduling: true,
 		},
@@ -358,7 +358,7 @@ func TestValidateCreate(t *testing.T) {
 				PodAnnotation(kueue.PodSetSliceSizeAnnotation, "1").
 				Obj(),
 			wantValidationErrs: field.ErrorList{
-				field.Forbidden(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "cannot be set when 'kueue.x-k8s.io/podset-slice-required-topology' is not present"),
+				field.Forbidden(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "may not be set when 'kueue.x-k8s.io/podset-slice-required-topology' is not specified"),
 			},
 			topologyAwareScheduling: true,
 		},
@@ -679,7 +679,7 @@ func TestValidateUpdate(t *testing.T) {
 				PodAnnotation(kueue.PodSetSliceRequiredTopologyAnnotation, "cloud.com/block").
 				Obj(),
 			wantValidationErrs: field.ErrorList{
-				field.Required(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "slice size is required if slice topology is requested"),
+				field.Required(replicaMetaPath.Child("annotations").Key("kueue.x-k8s.io/podset-slice-size"), "must be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 			},
 			topologyAwareScheduling: true,
 		},
diff --git a/pkg/controller/jobs/leaderworkerset/leaderworkerset_webhook_test.go b/pkg/controller/jobs/leaderworkerset/leaderworkerset_webhook_test.go
index afd629fb6..eb15ad9fe 100644
--- a/pkg/controller/jobs/leaderworkerset/leaderworkerset_webhook_test.go
+++ b/pkg/controller/jobs/leaderworkerset/leaderworkerset_webhook_test.go
@@ -280,9 +280,9 @@ func TestValidateCreate(t *testing.T) {
 				Obj(),
 			wantErr: field.ErrorList{
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.leaderTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "cannot be set when 'kueue.x-k8s.io/podset-slice-required-topology' is not present"),
+					Key("kueue.x-k8s.io/podset-slice-size"), "may not be set when 'kueue.x-k8s.io/podset-slice-required-topology' is not specified"),
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.workerTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "cannot be set when 'kueue.x-k8s.io/podset-slice-required-topology' is not present"),
+					Key("kueue.x-k8s.io/podset-slice-size"), "may not set when 'kueue.x-k8s.io/podset-slice-required-topology' is not specified"),
 			}.ToAggregate(),
 			topologyAwareScheduling: true,
 		},
@@ -307,13 +307,13 @@ func TestValidateCreate(t *testing.T) {
 				Obj(),
 			wantErr: field.ErrorList{
 				field.Required(field.NewPath("spec.leaderWorkerTemplate.leaderTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "slice size is required if slice topology is requested"),
+					Key("kueue.x-k8s.io/podset-slice-size"), "must be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 				field.Required(field.NewPath("spec.leaderWorkerTemplate.workerTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "slice size is required if slice topology is requested"),
+					Key("kueue.x-k8s.io/podset-slice-size"), "must be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 			}.ToAggregate(),
 			topologyAwareScheduling: true,
 		},
-		"invalid slice topology request - slicing requested together with grouping": {
+		"invalid slice topology request - grouping requested together with slicing": {
 			lws: testingleaderworkerset.MakeLeaderWorkerSet("test-lws", "").
 				Queue("test-queue").
 				LeaderTemplate(corev1.PodTemplateSpec{
@@ -338,13 +338,13 @@ func TestValidateCreate(t *testing.T) {
 				Obj(),
 			wantErr: field.ErrorList{
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.leaderTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "cannot be set when 'kueue.x-k8s.io/podset-group-name' is present"),
+					Key("kueue.x-k8s.io/podset-group-name"), "may not be set when 'kueue.x-k8s.io/podset-slice-size' is specified"),
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.leaderTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-required-topology"), "cannot be set when 'kueue.x-k8s.io/podset-group-name' is present"),
+					Key("kueue.x-k8s.io/podset-group-name"), "may not be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.workerTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-size"), "cannot be set when 'kueue.x-k8s.io/podset-group-name' is present"),
+					Key("kueue.x-k8s.io/podset-group-name"), "may not be set when 'kueue.x-k8s.io/podset-slice-size' is specified"),
 				field.Forbidden(field.NewPath("spec.leaderWorkerTemplate.workerTemplate.metadata.annotations").
-					Key("kueue.x-k8s.io/podset-slice-required-topology"), "cannot be set when 'kueue.x-k8s.io/podset-group-name' is present"),
+					Key("kueue.x-k8s.io/podset-group-name"), "may not be set when 'kueue.x-k8s.io/podset-slice-required-topology' is specified"),
 			}.ToAggregate(),
 			topologyAwareScheduling: true,
 		},
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_controller_test.go b/pkg/controller/jobs/trainjob/trainjob_controller_test.go
similarity index 100%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_controller_test.go
rename to pkg/controller/jobs/trainjob/trainjob_controller_test.go
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_multikueue_adapter_test.go b/pkg/controller/jobs/trainjob/trainjob_multikueue_adapter_test.go
similarity index 100%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_multikueue_adapter_test.go
rename to pkg/controller/jobs/trainjob/trainjob_multikueue_adapter_test.go
diff --git a/pkg/controller/jobs/kubeflow/trainjob/trainjob_webhook_test.go b/pkg/controller/jobs/trainjob/trainjob_webhook_test.go
similarity index 100%
rename from pkg/controller/jobs/kubeflow/trainjob/trainjob_webhook_test.go
rename to pkg/controller/jobs/trainjob/trainjob_webhook_test.go
diff --git a/test/e2e/customconfigs/reconcile_test.go b/test/e2e/customconfigs/reconcile_test.go
index 135cbe8bf..0c08a05ab 100644
--- a/test/e2e/customconfigs/reconcile_test.go
+++ b/test/e2e/customconfigs/reconcile_test.go
@@ -19,6 +19,7 @@ package customconfigse2e
 import (
 	"github.com/onsi/ginkgo/v2"
 	"github.com/onsi/gomega"
+	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -88,53 +89,59 @@ var _ = ginkgo.Describe("Job reconciliation with ManagedJobsNamespaceSelectorAlw
 		util.ExpectObjectToBeDeletedWithTimeout(ctx, k8sClient, rf, true, util.LongTimeout)
 	})
 
-	ginkgo.It("should not reconcile a job in the default (unmanaged) namespace", func() {
-		job := testingjob.MakeJob("unmanaged-job", metav1.NamespaceDefault).
-			Queue(kueue.LocalQueueName(lq.Name)).
-			Suspend(true).
-			Image(util.GetAgnHostImage(), util.BehaviorExitFast).
-			Obj()
+	ginkgo.When("Testing namespace selector behavior", func() {
+		var (
+			testJob *batchv1.Job
+			testPod *corev1.Pod
+		)
 
-		gomega.Expect(k8sClient.Create(ctx, job)).To(gomega.Succeed())
-		ginkgo.DeferCleanup(func() {
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, job, true)
+		ginkgo.AfterEach(func() {
+			util.ExpectObjectToBeDeleted(ctx, k8sClient, testJob, true)
+			util.ExpectObjectToBeDeleted(ctx, k8sClient, testPod, true)
 		})
 
-		wls := &kueue.WorkloadList{}
-		gomega.Expect(k8sClient.List(ctx, wls, client.InNamespace(metav1.NamespaceDefault))).To(gomega.Succeed())
-		gomega.Expect(wls.Items).To(gomega.BeEmpty(), "Expected no workload in unmanaged namespace")
-	})
-
-	ginkgo.It("should reconcile a job in managed namespace and create a workload", func() {
-		job := testingjob.MakeJob("managed-job", ns.Name).
-			Queue(kueue.LocalQueueName(lq.Name)).
-			Suspend(true).
-			Image(util.GetAgnHostImage(), util.BehaviorExitFast).
-			Obj()
+		ginkgo.It("should not reconcile a job in the default (unmanaged) namespace", func() {
+			testJob = testingjob.MakeJob("unmanaged-job", metav1.NamespaceDefault).
+				Queue(kueue.LocalQueueName(lq.Name)).
+				Suspend(true).
+				Image(util.GetAgnHostImage(), util.BehaviorExitFast).
+				Obj()
 
-		gomega.Expect(k8sClient.Create(ctx, job)).To(gomega.Succeed())
+			gomega.Expect(k8sClient.Create(ctx, testJob)).To(gomega.Succeed())
 
-		ginkgo.By("check that only one workload is created", func() {
-			createdWorkloads := &kueue.WorkloadList{}
-			gomega.Eventually(func(g gomega.Gomega) {
-				g.Expect(k8sClient.List(ctx, createdWorkloads, client.InNamespace(ns.Name))).To(gomega.Succeed())
-				g.Expect(createdWorkloads.Items).To(gomega.HaveLen(1))
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			wls := &kueue.WorkloadList{}
+			gomega.Expect(k8sClient.List(ctx, wls, client.InNamespace(metav1.NamespaceDefault))).To(gomega.Succeed())
+			gomega.Expect(wls.Items).To(gomega.BeEmpty(), "Expected no workload in unmanaged namespace")
 		})
-	})
 
-	ginkgo.It("Verify existing behavior - should not reconcile a pod in the default (unmanaged) namespace", func() {
-		testPod := testingpod.MakePod("test-pod", metav1.NamespaceDefault).
-			Queue(lq.Name).
-			Image(util.GetAgnHostImage(), util.BehaviorWaitForDeletion).
-			Obj()
-		gomega.Expect(k8sClient.Create(ctx, testPod)).To(gomega.Succeed())
-		ginkgo.DeferCleanup(func() {
-			util.ExpectObjectToBeDeleted(ctx, k8sClient, testPod, true)
+		ginkgo.It("should reconcile a job in managed namespace and create a workload", func() {
+			testJob = testingjob.MakeJob("managed-job", ns.Name).
+				Queue(kueue.LocalQueueName(lq.Name)).
+				Suspend(true).
+				Image(util.GetAgnHostImage(), util.BehaviorExitFast).
+				Obj()
+
+			gomega.Expect(k8sClient.Create(ctx, testJob)).To(gomega.Succeed())
+
+			ginkgo.By("check that only one workload is created", func() {
+				createdWorkloads := &kueue.WorkloadList{}
+				gomega.Eventually(func(g gomega.Gomega) {
+					g.Expect(k8sClient.List(ctx, createdWorkloads, client.InNamespace(ns.Name))).To(gomega.Succeed())
+					g.Expect(createdWorkloads.Items).To(gomega.HaveLen(1))
+				}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			})
 		})
 
-		wls := &kueue.WorkloadList{}
-		gomega.Expect(k8sClient.List(ctx, wls, client.InNamespace(metav1.NamespaceDefault))).To(gomega.Succeed())
-		gomega.Expect(wls.Items).To(gomega.BeEmpty(), "Expected no workload for pod in unmanaged namespace")
+		ginkgo.It("should not reconcile a pod in the default (unmanaged) namespace", func() {
+			testPod = testingpod.MakePod("test-pod", metav1.NamespaceDefault).
+				Queue(lq.Name).
+				Image(util.GetAgnHostImage(), util.BehaviorWaitForDeletion).
+				Obj()
+			gomega.Expect(k8sClient.Create(ctx, testPod)).To(gomega.Succeed())
+
+			wls := &kueue.WorkloadList{}
+			gomega.Expect(k8sClient.List(ctx, wls, client.InNamespace(metav1.NamespaceDefault))).To(gomega.Succeed())
+			gomega.Expect(wls.Items).To(gomega.BeEmpty(), "Expected no workload for pod in unmanaged namespace")
+		})
 	})
 })
diff --git a/test/e2e/kueueviz/package-lock.json b/test/e2e/kueueviz/package-lock.json
index 16478813b..33e3b53af 100644
--- a/test/e2e/kueueviz/package-lock.json
+++ b/test/e2e/kueueviz/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "kueueviz-frontend-tests",
-  "version": "0.13.4",
+  "version": "0.14.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "kueueviz-frontend-tests",
-      "version": "0.13.4",
+      "version": "0.14.0",
       "devDependencies": {
         "@testing-library/cypress": "^10.1.0",
         "cypress": "^15.3.0",
diff --git a/test/e2e/kueueviz/package.json b/test/e2e/kueueviz/package.json
index e77318e7c..206ab1607 100644
--- a/test/e2e/kueueviz/package.json
+++ b/test/e2e/kueueviz/package.json
@@ -1,6 +1,6 @@
 {
   "name": "kueueviz-frontend-tests",
-  "version": "0.13.4",
+  "version": "0.14.0",
   "scripts": {
     "cypress:run": "cypress run --headless",
     "check-unused": "depcheck"
diff --git a/test/e2e/multikueue/e2e_test.go b/test/e2e/multikueue/e2e_test.go
index 2e0bdbadd..83afe6d34 100644
--- a/test/e2e/multikueue/e2e_test.go
+++ b/test/e2e/multikueue/e2e_test.go
@@ -46,12 +46,12 @@ import (
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	workloadjobset "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
 	workloadpytorchjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
 	workloadmpijob "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
 	workloadpod "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	podconstants "sigs.k8s.io/kueue/pkg/controller/jobs/pod/constants"
 	workloadraycluster "sigs.k8s.io/kueue/pkg/controller/jobs/raycluster"
 	workloadrayjob "sigs.k8s.io/kueue/pkg/controller/jobs/rayjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
 	utilpod "sigs.k8s.io/kueue/pkg/util/pod"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
diff --git a/test/e2e/singlecluster/trainjob_test.go b/test/e2e/singlecluster/trainjob_test.go
index db55b2279..67c91040b 100644
--- a/test/e2e/singlecluster/trainjob_test.go
+++ b/test/e2e/singlecluster/trainjob_test.go
@@ -26,7 +26,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingtrainjob "sigs.k8s.io/kueue/pkg/util/testingjobs/trainjob"
 	"sigs.k8s.io/kueue/test/util"
diff --git a/test/integration/multikueue/jobs_test.go b/test/integration/multikueue/jobs_test.go
index 4d314f217..7e2664a42 100644
--- a/test/integration/multikueue/jobs_test.go
+++ b/test/integration/multikueue/jobs_test.go
@@ -51,11 +51,11 @@ import (
 	workloadpytorchjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
 	workloadtfjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/tfjob"
 	workloadxgboostjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/xgboostjob"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
 	workloadmpijob "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
 	workloadpod "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	workloadraycluster "sigs.k8s.io/kueue/pkg/controller/jobs/raycluster"
 	workloadrayjob "sigs.k8s.io/kueue/pkg/controller/jobs/rayjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
diff --git a/test/integration/multikueue/suite_test.go b/test/integration/multikueue/suite_test.go
index 14d0105dc..4ba15ee54 100644
--- a/test/integration/multikueue/suite_test.go
+++ b/test/integration/multikueue/suite_test.go
@@ -49,11 +49,11 @@ import (
 	workloadpytorchjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
 	workloadtfjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/tfjob"
 	workloadxgboostjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/xgboostjob"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
 	workloadmpijob "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
 	workloadpod "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	workloadraycluster "sigs.k8s.io/kueue/pkg/controller/jobs/raycluster"
 	workloadrayjob "sigs.k8s.io/kueue/pkg/controller/jobs/rayjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	dispatcher "sigs.k8s.io/kueue/pkg/controller/workloaddispatcher"
 	"sigs.k8s.io/kueue/pkg/util/kubeversion"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
diff --git a/test/integration/singlecluster/controller/jobs/trainjob/suite_test.go b/test/integration/singlecluster/controller/jobs/trainjob/suite_test.go
index 1dfd50e64..70018b80c 100644
--- a/test/integration/singlecluster/controller/jobs/trainjob/suite_test.go
+++ b/test/integration/singlecluster/controller/jobs/trainjob/suite_test.go
@@ -33,7 +33,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/core"
 	"sigs.k8s.io/kueue/pkg/controller/core/indexer"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
-	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
+	"sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	"sigs.k8s.io/kueue/pkg/scheduler"
 	"sigs.k8s.io/kueue/test/integration/framework"
 	"sigs.k8s.io/kueue/test/util"
diff --git a/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go b/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go
index 58c7bcfaf..df5da554b 100644
--- a/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go
@@ -33,7 +33,7 @@ import (
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	testingjobset "sigs.k8s.io/kueue/pkg/util/testingjobs/jobset"
 	testingtrainjob "sigs.k8s.io/kueue/pkg/util/testingjobs/trainjob"
diff --git a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
index c45469a2f..852c46227 100644
--- a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
+++ b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
@@ -493,6 +493,7 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 	ginkgo.When("using hierarchical cohorts with several flavors", func() {
 		var (
 			cqp1 *kueue.ClusterQueue
+			cqp5 *kueue.ClusterQueue
 		)
 		ginkgo.BeforeEach(func() {
 			createCohort(testing.MakeCohort("root-cohort").Obj())
@@ -566,7 +567,7 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 				Preemption(preemption).
 				Obj())
 
-			createQueue(testing.MakeClusterQueue("cq-p5").
+			cqp5 = createQueue(testing.MakeClusterQueue("cq-p5").
 				Cohort("cohort-b").
 				FairWeight(resource.MustParse("1")).
 				ResourceGroup(
@@ -614,6 +615,38 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			util.ExpectClusterQueueWeightedShareMetric(cqp1, 600)
 			expectCohortWeightedShare("cohort-a", 0)
 		})
+
+		// scenario from Kueue#7015
+		// WeightedShare(CohortA) = 0/20 * 1000 = 0
+		// WeightedShare(cq-p1)  = 12/20 * 1000 = 600
+		// WeightedShare(CohortB) = 0/20 * 1000 = 0
+		// WeightedShare(cq-p5)   = 2/20 * 1000 = 100
+		ginkgo.It("CohortB preempts and schedules in flavor which has guarantees", func() {
+			_ = features.SetEnable(features.FlavorFungibilityImplicitPreferenceDefault, true)
+			ginkgo.By("Create workload which saturate all cohort resources")
+			for range 20 {
+				createWorkload("cq-p1", "1")
+			}
+			util.ExpectReservingActiveWorkloadsMetric(cqp1, 20)
+			expectCohortWeightedShare("cohort-a", 100)
+
+			ginkgo.By("Create workloads in CohortB which will preempt CohortA")
+			createWorkload("cq-p5", "1")
+			createWorkload("cq-p5", "1")
+
+			ginkgo.By("Finish Preemption of Workloads")
+			util.FinishEvictionOfWorkloadsInCQ(ctx, k8sClient, cqp1, 2)
+
+			ginkgo.By("Check expected workloads active")
+			util.ExpectReservingActiveWorkloadsMetric(cqp1, 18)
+			util.ExpectReservingActiveWorkloadsMetric(cqp5, 2)
+
+			ginkgo.By("Expected Weighted Shares")
+			util.ExpectClusterQueueWeightedShareMetric(cqp1, 600)
+			util.ExpectClusterQueueWeightedShareMetric(cqp5, 100)
+			expectCohortWeightedShare("cohort-a", 0)
+			expectCohortWeightedShare("cohort-b", 0)
+		})
 	})
 
 	ginkgo.When("Using AdmissionFairSharing at ClusterQueue level", func() {
diff --git a/test/integration/singlecluster/webhook/jobs/trainjob_webhook_test.go b/test/integration/singlecluster/webhook/jobs/trainjob_webhook_test.go
index 8e794170f..79d05bbf1 100644
--- a/test/integration/singlecluster/webhook/jobs/trainjob_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/trainjob_webhook_test.go
@@ -25,7 +25,7 @@ import (
 	"k8s.io/utils/ptr"
 
 	controllerconstants "sigs.k8s.io/kueue/pkg/controller/constants"
-	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/trainjob"
+	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
 	testingtrainjob "sigs.k8s.io/kueue/pkg/util/testingjobs/trainjob"
 	"sigs.k8s.io/kueue/test/util"
 )