Repository: kubernetes-sigs/kueue

Issue:
Add performance tests for v1beta2 TopologyAssignment encoding
**What would you like to be cleaned**:

#7544 adds `V1Beta2From()` to convert "internal" representation of a TopologyAssignment to the "public" v1beta2 format, aiming at fitting more nodes within a single etcd entry.

Ideally I'd like to have "performance" tests for this, of 2 kinds:

- verifying that, at least for the most popular node naming schemes, we can fit 60k nodes
- verifying that the conversion itself does not take too much time (a Golang benchmark)

**Why is this needed**:

This will have some value right away, plus it will become helpful once we consider switching to more sophisticated encoding approaches (i.e. using multiple TopologyAssignmentSlices).

Hints from issue comments:
@olekzabl: /assign

Solution patch:
diff --git a/apis/config/v1beta1/configuration_conversion.go b/apis/config/v1beta1/configuration_conversion.go
index 6f6449b0a..15c71cc73 100644
--- a/apis/config/v1beta1/configuration_conversion.go
+++ b/apis/config/v1beta1/configuration_conversion.go
@@ -37,10 +37,25 @@ func (dst *Configuration) ConvertFrom(srcRaw conversion.Hub) error {
 }
 
 func Convert_v1beta1_Configuration_To_v1beta2_Configuration(in *Configuration, out *v1beta2.Configuration, s conversionapi.Scope) error {
-	return autoConvert_v1beta1_Configuration_To_v1beta2_Configuration(in, out, s)
+	if err := autoConvert_v1beta1_Configuration_To_v1beta2_Configuration(in, out, s); err != nil {
+		return err
+	}
+	if in.FairSharing == nil || !in.FairSharing.Enable {
+		out.FairSharing = nil
+	}
+	return nil
 }
 
 // Convert_v1beta1_Integrations_To_v1beta2_Integrations is a conversion function that ignores deprecated PodOptions field.
 func Convert_v1beta1_Integrations_To_v1beta2_Integrations(in *Integrations, out *v1beta2.Integrations, s conversionapi.Scope) error {
 	return autoConvert_v1beta1_Integrations_To_v1beta2_Integrations(in, out, s)
 }
+
+func Convert_v1beta1_FairSharing_To_v1beta2_FairSharing(in *FairSharing, out *v1beta2.FairSharing, s conversionapi.Scope) error {
+	return autoConvert_v1beta1_FairSharing_To_v1beta2_FairSharing(in, out, s)
+}
+
+func Convert_v1beta2_FairSharing_To_v1beta1_FairSharing(in *v1beta2.FairSharing, out *FairSharing, s conversionapi.Scope) error {
+	out.Enable = true
+	return autoConvert_v1beta2_FairSharing_To_v1beta1_FairSharing(in, out, s)
+}
diff --git a/apis/config/v1beta1/zz_generated.conversion.go b/apis/config/v1beta1/zz_generated.conversion.go
index 61e2ec2af..77f5b798f 100644
--- a/apis/config/v1beta1/zz_generated.conversion.go
+++ b/apis/config/v1beta1/zz_generated.conversion.go
@@ -124,16 +124,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddGeneratedConversionFunc((*FairSharing)(nil), (*v1beta2.FairSharing)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_FairSharing_To_v1beta2_FairSharing(a.(*FairSharing), b.(*v1beta2.FairSharing), scope)
-	}); err != nil {
-		return err
-	}
-	if err := s.AddGeneratedConversionFunc((*v1beta2.FairSharing)(nil), (*FairSharing)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta2_FairSharing_To_v1beta1_FairSharing(a.(*v1beta2.FairSharing), b.(*FairSharing), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddGeneratedConversionFunc((*v1beta2.Integrations)(nil), (*Integrations)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta2_Integrations_To_v1beta1_Integrations(a.(*v1beta2.Integrations), b.(*Integrations), scope)
 	}); err != nil {
@@ -234,11 +224,21 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddConversionFunc((*FairSharing)(nil), (*v1beta2.FairSharing)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_FairSharing_To_v1beta2_FairSharing(a.(*FairSharing), b.(*v1beta2.FairSharing), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddConversionFunc((*Integrations)(nil), (*v1beta2.Integrations)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_Integrations_To_v1beta2_Integrations(a.(*Integrations), b.(*v1beta2.Integrations), scope)
 	}); err != nil {
 		return err
 	}
+	if err := s.AddConversionFunc((*v1beta2.FairSharing)(nil), (*FairSharing)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_FairSharing_To_v1beta1_FairSharing(a.(*v1beta2.FairSharing), b.(*FairSharing), scope)
+	}); err != nil {
+		return err
+	}
 	return nil
 }
 
@@ -309,7 +309,15 @@ func autoConvert_v1beta1_Configuration_To_v1beta2_Configuration(in *Configuratio
 	}
 	// WARNING: in.QueueVisibility requires manual conversion: does not exist in peer-type
 	out.MultiKueue = (*v1beta2.MultiKueue)(unsafe.Pointer(in.MultiKueue))
-	out.FairSharing = (*v1beta2.FairSharing)(unsafe.Pointer(in.FairSharing))
+	if in.FairSharing != nil {
+		in, out := &in.FairSharing, &out.FairSharing
+		*out = new(v1beta2.FairSharing)
+		if err := Convert_v1beta1_FairSharing_To_v1beta2_FairSharing(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.FairSharing = nil
+	}
 	out.AdmissionFairSharing = (*v1beta2.AdmissionFairSharing)(unsafe.Pointer(in.AdmissionFairSharing))
 	out.Resources = (*v1beta2.Resources)(unsafe.Pointer(in.Resources))
 	out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
@@ -337,7 +345,15 @@ func autoConvert_v1beta2_Configuration_To_v1beta1_Configuration(in *v1beta2.Conf
 		out.Integrations = nil
 	}
 	out.MultiKueue = (*MultiKueue)(unsafe.Pointer(in.MultiKueue))
-	out.FairSharing = (*FairSharing)(unsafe.Pointer(in.FairSharing))
+	if in.FairSharing != nil {
+		in, out := &in.FairSharing, &out.FairSharing
+		*out = new(FairSharing)
+		if err := Convert_v1beta2_FairSharing_To_v1beta1_FairSharing(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.FairSharing = nil
+	}
 	out.AdmissionFairSharing = (*AdmissionFairSharing)(unsafe.Pointer(in.AdmissionFairSharing))
 	out.Resources = (*Resources)(unsafe.Pointer(in.Resources))
 	out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
@@ -507,27 +523,16 @@ func Convert_v1beta2_DeviceClassMapping_To_v1beta1_DeviceClassMapping(in *v1beta
 }
 
 func autoConvert_v1beta1_FairSharing_To_v1beta2_FairSharing(in *FairSharing, out *v1beta2.FairSharing, s conversion.Scope) error {
-	out.Enable = in.Enable
+	// WARNING: in.Enable requires manual conversion: does not exist in peer-type
 	out.PreemptionStrategies = *(*[]v1beta2.PreemptionStrategy)(unsafe.Pointer(&in.PreemptionStrategies))
 	return nil
 }
 
-// Convert_v1beta1_FairSharing_To_v1beta2_FairSharing is an autogenerated conversion function.
-func Convert_v1beta1_FairSharing_To_v1beta2_FairSharing(in *FairSharing, out *v1beta2.FairSharing, s conversion.Scope) error {
-	return autoConvert_v1beta1_FairSharing_To_v1beta2_FairSharing(in, out, s)
-}
-
 func autoConvert_v1beta2_FairSharing_To_v1beta1_FairSharing(in *v1beta2.FairSharing, out *FairSharing, s conversion.Scope) error {
-	out.Enable = in.Enable
 	out.PreemptionStrategies = *(*[]PreemptionStrategy)(unsafe.Pointer(&in.PreemptionStrategies))
 	return nil
 }
 
-// Convert_v1beta2_FairSharing_To_v1beta1_FairSharing is an autogenerated conversion function.
-func Convert_v1beta2_FairSharing_To_v1beta1_FairSharing(in *v1beta2.FairSharing, out *FairSharing, s conversion.Scope) error {
-	return autoConvert_v1beta2_FairSharing_To_v1beta1_FairSharing(in, out, s)
-}
-
 func autoConvert_v1beta1_Integrations_To_v1beta2_Integrations(in *Integrations, out *v1beta2.Integrations, s conversion.Scope) error {
 	out.Frameworks = *(*[]string)(unsafe.Pointer(&in.Frameworks))
 	out.ExternalFrameworks = *(*[]string)(unsafe.Pointer(&in.ExternalFrameworks))
diff --git a/apis/config/v1beta2/configuration_types.go b/apis/config/v1beta2/configuration_types.go
index bf450a6a5..3678e00e2 100644
--- a/apis/config/v1beta2/configuration_types.go
+++ b/apis/config/v1beta2/configuration_types.go
@@ -480,10 +480,6 @@ const (
 )
 
 type FairSharing struct {
-	// enable indicates whether to enable Fair Sharing for all cohorts.
-	// Defaults to false.
-	Enable bool `json:"enable"`
-
 	// preemptionStrategies indicates which constraints should a preemption satisfy.
 	// The preemption algorithm will only use the next strategy in the list if the
 	// incoming workload (preemptor) doesn't fit after using the previous strategies.
diff --git a/apis/config/v1beta2/defaults.go b/apis/config/v1beta2/defaults.go
index cbbcbd821..877cc2110 100644
--- a/apis/config/v1beta2/defaults.go
+++ b/apis/config/v1beta2/defaults.go
@@ -123,7 +123,7 @@ func SetDefaults_Configuration(cfg *Configuration) {
 	cfg.MultiKueue.WorkerLostTimeout = cmp.Or(cfg.MultiKueue.WorkerLostTimeout, &metav1.Duration{Duration: DefaultMultiKueueWorkerLostTimeout})
 	cfg.MultiKueue.DispatcherName = cmp.Or(cfg.MultiKueue.DispatcherName, ptr.To(MultiKueueDispatcherModeAllAtOnce))
 
-	if fs := cfg.FairSharing; fs != nil && fs.Enable && len(fs.PreemptionStrategies) == 0 {
+	if fs := cfg.FairSharing; fs != nil && len(fs.PreemptionStrategies) == 0 {
 		fs.PreemptionStrategies = []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare}
 	}
 	if afs := cfg.AdmissionFairSharing; afs != nil {
diff --git a/apis/kueue/v1beta1/workload_conversion.go b/apis/kueue/v1beta1/workload_conversion.go
index 729b7f863..b32330e80 100644
--- a/apis/kueue/v1beta1/workload_conversion.go
+++ b/apis/kueue/v1beta1/workload_conversion.go
@@ -21,6 +21,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/conversion"
 
 	"sigs.k8s.io/kueue/apis/kueue/v1beta2"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 )
 
 //lint:file-ignore ST1003 "generated Convert_* calls below use underscores"
@@ -69,3 +70,32 @@ func Convert_v1beta2_WorkloadSpec_To_v1beta1_WorkloadSpec(in *v1beta2.WorkloadSp
 	}
 	return autoConvert_v1beta2_WorkloadSpec_To_v1beta1_WorkloadSpec(in, out, s)
 }
+
+func Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(in *TopologyAssignment, out *v1beta2.TopologyAssignment, s conversionapi.Scope) error {
+	ta := &tas.TopologyAssignment{
+		Levels:  in.Levels,
+		Domains: make([]tas.TopologyDomainAssignment, 0, len(in.Domains)),
+	}
+	for _, domain := range in.Domains {
+		ta.Domains = append(ta.Domains, tas.TopologyDomainAssignment{
+			Values: domain.Values,
+			Count:  domain.Count,
+		})
+	}
+	v2 := tas.V1Beta2From(ta)
+	out.Levels = v2.Levels
+	out.Slices = v2.Slices
+	return nil
+}
+
+func Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(in *v1beta2.TopologyAssignment, out *TopologyAssignment, s conversionapi.Scope) error {
+	out.Levels = in.Levels
+	out.Domains = make([]TopologyDomainAssignment, 0, tas.TotalDomainCount(in))
+	for req := range tas.InternalSeqFrom(in) {
+		out.Domains = append(out.Domains, TopologyDomainAssignment{
+			Values: req.Values,
+			Count:  req.Count,
+		})
+	}
+	return nil
+}
diff --git a/apis/kueue/v1beta1/zz_generated.conversion.go b/apis/kueue/v1beta1/zz_generated.conversion.go
index 3ae4e0220..ee4b57db6 100644
--- a/apis/kueue/v1beta1/zz_generated.conversion.go
+++ b/apis/kueue/v1beta1/zz_generated.conversion.go
@@ -618,26 +618,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddGeneratedConversionFunc((*TopologyAssignment)(nil), (*v1beta2.TopologyAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(a.(*TopologyAssignment), b.(*v1beta2.TopologyAssignment), scope)
-	}); err != nil {
-		return err
-	}
-	if err := s.AddGeneratedConversionFunc((*v1beta2.TopologyAssignment)(nil), (*TopologyAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(a.(*v1beta2.TopologyAssignment), b.(*TopologyAssignment), scope)
-	}); err != nil {
-		return err
-	}
-	if err := s.AddGeneratedConversionFunc((*TopologyDomainAssignment)(nil), (*v1beta2.TopologyDomainAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta1_TopologyDomainAssignment_To_v1beta2_TopologyDomainAssignment(a.(*TopologyDomainAssignment), b.(*v1beta2.TopologyDomainAssignment), scope)
-	}); err != nil {
-		return err
-	}
-	if err := s.AddGeneratedConversionFunc((*v1beta2.TopologyDomainAssignment)(nil), (*TopologyDomainAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_v1beta2_TopologyDomainAssignment_To_v1beta1_TopologyDomainAssignment(a.(*v1beta2.TopologyDomainAssignment), b.(*TopologyDomainAssignment), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddGeneratedConversionFunc((*TopologyInfo)(nil), (*v1beta2.TopologyInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_TopologyInfo_To_v1beta2_TopologyInfo(a.(*TopologyInfo), b.(*v1beta2.TopologyInfo), scope)
 	}); err != nil {
@@ -758,6 +738,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddConversionFunc((*TopologyAssignment)(nil), (*v1beta2.TopologyAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(a.(*TopologyAssignment), b.(*v1beta2.TopologyAssignment), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddConversionFunc((*WorkloadSpec)(nil), (*v1beta2.WorkloadSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_WorkloadSpec_To_v1beta2_WorkloadSpec(a.(*WorkloadSpec), b.(*v1beta2.WorkloadSpec), scope)
 	}); err != nil {
@@ -778,6 +763,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddConversionFunc((*v1beta2.TopologyAssignment)(nil), (*TopologyAssignment)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(a.(*v1beta2.TopologyAssignment), b.(*TopologyAssignment), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddConversionFunc((*v1beta2.WorkloadSpec)(nil), (*WorkloadSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta2_WorkloadSpec_To_v1beta1_WorkloadSpec(a.(*v1beta2.WorkloadSpec), b.(*WorkloadSpec), scope)
 	}); err != nil {
@@ -793,7 +783,17 @@ func RegisterConversions(s *runtime.Scheme) error {
 
 func autoConvert_v1beta1_Admission_To_v1beta2_Admission(in *Admission, out *v1beta2.Admission, s conversion.Scope) error {
 	out.ClusterQueue = v1beta2.ClusterQueueReference(in.ClusterQueue)
-	out.PodSetAssignments = *(*[]v1beta2.PodSetAssignment)(unsafe.Pointer(&in.PodSetAssignments))
+	if in.PodSetAssignments != nil {
+		in, out := &in.PodSetAssignments, &out.PodSetAssignments
+		*out = make([]v1beta2.PodSetAssignment, len(*in))
+		for i := range *in {
+			if err := Convert_v1beta1_PodSetAssignment_To_v1beta2_PodSetAssignment(&(*in)[i], &(*out)[i], s); err != nil {
+				return err
+			}
+		}
+	} else {
+		out.PodSetAssignments = nil
+	}
 	return nil
 }
 
@@ -804,7 +804,17 @@ func Convert_v1beta1_Admission_To_v1beta2_Admission(in *Admission, out *v1beta2.
 
 func autoConvert_v1beta2_Admission_To_v1beta1_Admission(in *v1beta2.Admission, out *Admission, s conversion.Scope) error {
 	out.ClusterQueue = ClusterQueueReference(in.ClusterQueue)
-	out.PodSetAssignments = *(*[]PodSetAssignment)(unsafe.Pointer(&in.PodSetAssignments))
+	if in.PodSetAssignments != nil {
+		in, out := &in.PodSetAssignments, &out.PodSetAssignments
+		*out = make([]PodSetAssignment, len(*in))
+		for i := range *in {
+			if err := Convert_v1beta2_PodSetAssignment_To_v1beta1_PodSetAssignment(&(*in)[i], &(*out)[i], s); err != nil {
+				return err
+			}
+		}
+	} else {
+		out.PodSetAssignments = nil
+	}
 	return nil
 }
 
@@ -1830,7 +1840,15 @@ func autoConvert_v1beta1_PodSetAssignment_To_v1beta2_PodSetAssignment(in *PodSet
 	out.Flavors = *(*map[corev1.ResourceName]v1beta2.ResourceFlavorReference)(unsafe.Pointer(&in.Flavors))
 	out.ResourceUsage = *(*corev1.ResourceList)(unsafe.Pointer(&in.ResourceUsage))
 	out.Count = (*int32)(unsafe.Pointer(in.Count))
-	out.TopologyAssignment = (*v1beta2.TopologyAssignment)(unsafe.Pointer(in.TopologyAssignment))
+	if in.TopologyAssignment != nil {
+		in, out := &in.TopologyAssignment, &out.TopologyAssignment
+		*out = new(v1beta2.TopologyAssignment)
+		if err := Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.TopologyAssignment = nil
+	}
 	out.DelayedTopologyRequest = (*v1beta2.DelayedTopologyRequestState)(unsafe.Pointer(in.DelayedTopologyRequest))
 	return nil
 }
@@ -1845,7 +1863,15 @@ func autoConvert_v1beta2_PodSetAssignment_To_v1beta1_PodSetAssignment(in *v1beta
 	out.Flavors = *(*map[corev1.ResourceName]ResourceFlavorReference)(unsafe.Pointer(&in.Flavors))
 	out.ResourceUsage = *(*corev1.ResourceList)(unsafe.Pointer(&in.ResourceUsage))
 	out.Count = (*int32)(unsafe.Pointer(in.Count))
-	out.TopologyAssignment = (*TopologyAssignment)(unsafe.Pointer(in.TopologyAssignment))
+	if in.TopologyAssignment != nil {
+		in, out := &in.TopologyAssignment, &out.TopologyAssignment
+		*out = new(TopologyAssignment)
+		if err := Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.TopologyAssignment = nil
+	}
 	out.DelayedTopologyRequest = (*DelayedTopologyRequestState)(unsafe.Pointer(in.DelayedTopologyRequest))
 	return nil
 }
@@ -2323,48 +2349,16 @@ func Convert_v1beta2_Topology_To_v1beta1_Topology(in *v1beta2.Topology, out *Top
 
 func autoConvert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(in *TopologyAssignment, out *v1beta2.TopologyAssignment, s conversion.Scope) error {
 	out.Levels = *(*[]string)(unsafe.Pointer(&in.Levels))
-	out.Domains = *(*[]v1beta2.TopologyDomainAssignment)(unsafe.Pointer(&in.Domains))
+	// WARNING: in.Domains requires manual conversion: does not exist in peer-type
 	return nil
 }
 
-// Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment is an autogenerated conversion function.
-func Convert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(in *TopologyAssignment, out *v1beta2.TopologyAssignment, s conversion.Scope) error {
-	return autoConvert_v1beta1_TopologyAssignment_To_v1beta2_TopologyAssignment(in, out, s)
-}
-
 func autoConvert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(in *v1beta2.TopologyAssignment, out *TopologyAssignment, s conversion.Scope) error {
 	out.Levels = *(*[]string)(unsafe.Pointer(&in.Levels))
-	out.Domains = *(*[]TopologyDomainAssignment)(unsafe.Pointer(&in.Domains))
+	// WARNING: in.Slices requires manual conversion: does not exist in peer-type
 	return nil
 }
 
-// Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment is an autogenerated conversion function.
-func Convert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(in *v1beta2.TopologyAssignment, out *TopologyAssignment, s conversion.Scope) error {
-	return autoConvert_v1beta2_TopologyAssignment_To_v1beta1_TopologyAssignment(in, out, s)
-}
-
-func autoConvert_v1beta1_TopologyDomainAssignment_To_v1beta2_TopologyDomainAssignment(in *TopologyDomainAssignment, out *v1beta2.TopologyDomainAssignment, s conversion.Scope) error {
-	out.Values = *(*[]string)(unsafe.Pointer(&in.Values))
-	out.Count = in.Count
-	return nil
-}
-
-// Convert_v1beta1_TopologyDomainAssignment_To_v1beta2_TopologyDomainAssignment is an autogenerated conversion function.
-func Convert_v1beta1_TopologyDomainAssignment_To_v1beta2_TopologyDomainAssignment(in *TopologyDomainAssignment, out *v1beta2.TopologyDomainAssignment, s conversion.Scope) error {
-	return autoConvert_v1beta1_TopologyDomainAssignment_To_v1beta2_TopologyDomainAssignment(in, out, s)
-}
-
-func autoConvert_v1beta2_TopologyDomainAssignment_To_v1beta1_TopologyDomainAssignment(in *v1beta2.TopologyDomainAssignment, out *TopologyDomainAssignment, s conversion.Scope) error {
-	out.Values = *(*[]string)(unsafe.Pointer(&in.Values))
-	out.Count = in.Count
-	return nil
-}
-
-// Convert_v1beta2_TopologyDomainAssignment_To_v1beta1_TopologyDomainAssignment is an autogenerated conversion function.
-func Convert_v1beta2_TopologyDomainAssignment_To_v1beta1_TopologyDomainAssignment(in *v1beta2.TopologyDomainAssignment, out *TopologyDomainAssignment, s conversion.Scope) error {
-	return autoConvert_v1beta2_TopologyDomainAssignment_To_v1beta1_TopologyDomainAssignment(in, out, s)
-}
-
 func autoConvert_v1beta1_TopologyInfo_To_v1beta2_TopologyInfo(in *TopologyInfo, out *v1beta2.TopologyInfo, s conversion.Scope) error {
 	out.Name = v1beta2.TopologyReference(in.Name)
 	out.Levels = *(*[]string)(unsafe.Pointer(&in.Levels))
@@ -2636,7 +2630,15 @@ func autoConvert_v1beta2_WorkloadSpec_To_v1beta1_WorkloadSpec(in *v1beta2.Worklo
 
 func autoConvert_v1beta1_WorkloadStatus_To_v1beta2_WorkloadStatus(in *WorkloadStatus, out *v1beta2.WorkloadStatus, s conversion.Scope) error {
 	out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
-	out.Admission = (*v1beta2.Admission)(unsafe.Pointer(in.Admission))
+	if in.Admission != nil {
+		in, out := &in.Admission, &out.Admission
+		*out = new(v1beta2.Admission)
+		if err := Convert_v1beta1_Admission_To_v1beta2_Admission(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.Admission = nil
+	}
 	out.RequeueState = (*v1beta2.RequeueState)(unsafe.Pointer(in.RequeueState))
 	out.ReclaimablePods = *(*[]v1beta2.ReclaimablePod)(unsafe.Pointer(&in.ReclaimablePods))
 	out.AdmissionChecks = *(*[]v1beta2.AdmissionCheckState)(unsafe.Pointer(&in.AdmissionChecks))
@@ -2651,7 +2653,15 @@ func autoConvert_v1beta1_WorkloadStatus_To_v1beta2_WorkloadStatus(in *WorkloadSt
 
 func autoConvert_v1beta2_WorkloadStatus_To_v1beta1_WorkloadStatus(in *v1beta2.WorkloadStatus, out *WorkloadStatus, s conversion.Scope) error {
 	out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
-	out.Admission = (*Admission)(unsafe.Pointer(in.Admission))
+	if in.Admission != nil {
+		in, out := &in.Admission, &out.Admission
+		*out = new(Admission)
+		if err := Convert_v1beta2_Admission_To_v1beta1_Admission(*in, *out, s); err != nil {
+			return err
+		}
+	} else {
+		out.Admission = nil
+	}
 	out.RequeueState = (*RequeueState)(unsafe.Pointer(in.RequeueState))
 	out.ReclaimablePods = *(*[]ReclaimablePod)(unsafe.Pointer(&in.ReclaimablePods))
 	out.AdmissionChecks = *(*[]AdmissionCheckState)(unsafe.Pointer(&in.AdmissionChecks))
diff --git a/apis/kueue/v1beta2/workload_types.go b/apis/kueue/v1beta2/workload_types.go
index 825eea6a6..ca9ad1023 100644
--- a/apis/kueue/v1beta2/workload_types.go
+++ b/apis/kueue/v1beta2/workload_types.go
@@ -278,46 +278,113 @@ type PodSetAssignment struct {
 	// topology domains corresponding to the lowest level of the topology.
 	// The assignment specifies the number of Pods to be scheduled per topology
 	// domain and specifies the node selectors for each topology domain, in the
-	// following way: the node selector keys are specified by the levels field
-	// (same for all domains), and the corresponding node selector value is
-	// specified by the domains.values subfield. If the TopologySpec.Levels field contains
-	// "kubernetes.io/hostname" label, topologyAssignment will contain data only for
-	// this label, and omit higher levels in the topology
+	// following way:
+	// * `levels` specifies the node selector keys (same for all domains).
+	//   - If the TopologySpec.Levels field contains "kubernetes.io/hostname" label,
+	//     topologyAssignment will contain data only for this label,
+	//     and omit higher levels in the topology.
+	// * `slices` specifies the node selector values and pod counts for all domains
+	//   (which may be partitioned into separate slices).
+	//   - The node selector values are arranged first by topology level, only then by domain.
+	//     (This allows "optimizing" similar values; see below).
+	// * The format of `slices` supports the following variations
+	//   (aimed to optimize the total bytesize for very large number of domains; see examples below):
+	//   - When all node selector values (at a given topology level, in a given slice)
+	//     share a common prefix and/or suffix, these may be stored
+	//     in dedicated `commonPrefix`/`commonSuffix` fields.
+	//     If so, the array of `roots` will only store the remaining parts of these strings.
+	//   - When all node selector values (at a given topology level, in a given slice)
+	//     are identical, this may be represented by `universal` value.
+	//   - When all pod counts (in a given slice) are identical,
+	//     this may be represented by `universal` pod count.
+	//
+	// Example 1:
+	//
+	// The following represents an assignment in which:
+	// * 4 Pods are to be scheduled on nodes matching the node selector:
+	//   - cloud.provider.com/topology-block: block-1
+	//   - cloud.provider.com/topology-rack: rack-1
+	// * 2 Pods are to be scheduled on nodes matching the node selector:
+	//   - cloud.provider.com/topology-block: block-1
+	//   - cloud.provider.com/topology-rack: rack-2
 	//
-	// Example:
+	// topologyAssignment:
+	//   levels:
+	//   - cloud.provider.com/topology-block
+	//   - cloud.provider.com/topology-rack
+	//   slices:
+	//   - domainCount: 2
+	//     valuesPerLevel:
+	//     - individual:
+	//         roots: [block-1, block-1]
+	//     - individual:
+	//         roots: [rack-1, rack-2]
+	//     podCounts:
+	//       individual: [4, 2]
+	//
+	// Example 2:
+	//
+	// The following is equivalent to Example 1 - but using extracted prefix and universalValue.
 	//
 	// topologyAssignment:
 	//   levels:
 	//   - cloud.provider.com/topology-block
 	//   - cloud.provider.com/topology-rack
-	//   domains:
-	//   - values: [block-1, rack-1]
-	//     count: 4
-	//   - values: [block-1, rack-2]
-	//     count: 2
-	//
-	// Here:
-	// - 4 Pods are to be scheduled on nodes matching the node selector:
-	//   cloud.provider.com/topology-block: block-1
-	//   cloud.provider.com/topology-rack: rack-1
-	// - 2 Pods are to be scheduled on nodes matching the node selector:
-	//   cloud.provider.com/topology-block: block-1
-	//   cloud.provider.com/topology-rack: rack-2
-	//
-	// Example:
-	// Below there is an equivalent of the above example assuming, Topology
-	// object defines kubernetes.io/hostname as the lowest level in topology.
-	// Hence we omit higher level of topologies, since the hostname label
-	// is sufficient to explicitly identify a proper node.
+	//   slices:
+	//   - domainCount: 2
+	//     valuesPerLevel:
+	//     - universal: block-1
+	//     - individual:
+	//         prefix: rack-
+	// 		   roots: [1, 2]
+	//     podCounts:
+	//       individual: [4, 2]
+	//
+	// Example 3:
+	//
+	// Now suppose that:
+	// - the Topology object defines kubernetes.io/hostname as the lowest level
+	//   (and hence, in the topologyAssignment, we omit all other levels
+	//   since the hostname label suffices to explicitly identify a proper node),
+	// - we assign 1 Pod per each node,
+	// - the node naming scheme is `block-{blockId}-rack-{rackId}-node-{nodeId}`.
+	// Then, using the "extraction of commons", the assignment from Examples 1-2 would look as follows:
+	//
+	// topologyAssignment:
+	//   levels:
+	//   - kubernetes.io/hostname
+	//   slices:
+	//   - domainCount: 6
+	//     valuesPerLevel:
+	//     - individual:
+	//         prefix: block-1-rack-
+	// 		   roots: [1-node-1, 1-node-2, 1-node-3, 1-node-4, 2-node-1, 2-node-2]
+	//     podCounts:
+	//       universal: 1
+	//
+	// Example 4:
+	//
+	// By using multiple slices, we can afford even longer common prefixes.
+	// The assignment from Example 3 can be alternatively represented as follows:
 	//
 	// topologyAssignment:
 	//   levels:
 	//   - kubernetes.io/hostname
-	//   domains:
-	//   - values: [hostname-1]
-	//     count: 4
-	//   - values: [hostname-2]
-	//     count: 2
+	//   slices:
+	//   - domainCount: 4
+	//     valuesPerLevel:
+	//     - individual:
+	//         prefix: block-1-rack-1-node-
+	// 		   roots: [1, 2, 3, 4]
+	//     podCounts:
+	//       universal: 1
+	//   - domainCount: 2
+	//     valuesPerLevel:
+	//     - individual:
+	//         prefix: block-1-rack-2-node-
+	// 		   roots: [1, 2]
+	//     podCounts:
+	//       universal: 1
 	//
 	// +optional
 	TopologyAssignment *TopologyAssignment `json:"topologyAssignment,omitempty"`
@@ -347,6 +414,7 @@ const (
 	DelayedTopologyRequestStateReady DelayedTopologyRequestState = "Ready"
 )
 
+// +kubebuilder:validation:XValidation:rule="self.slices.all(x, size(x.valuesPerLevel) == size(self.levels))", message="valuesPerLevel must have the same length as the number of levels in this TopologyAssignment"
 type TopologyAssignment struct {
 	// levels is an ordered list of keys denoting the levels of the assigned
 	// topology (i.e. node label keys), from the highest to the lowest level of
@@ -359,33 +427,90 @@ type TopologyAssignment struct {
 	// +kubebuilder:validation:items:MaxLength=317
 	Levels []string `json:"levels,omitempty"`
 
-	// domains is a list of topology assignments split by topology domains at
-	// the lowest level of the topology.
-	//
+	// slices represent topology assignments for subsets of pods of a workload.
+	// The full assignment is obtained as a union of all slices.
 	// +required
 	// +listType=atomic
-	// +kubebuilder:validation:MaxItems=100000
-	Domains []TopologyDomainAssignment `json:"domains,omitempty"`
+	// +kubebuilder:validation:MaxItems=1000
+	Slices []TopologyAssignmentSlice `json:"slices,omitempty"`
 }
 
-type TopologyDomainAssignment struct {
-	// values is an ordered list of node selector values describing a topology
-	// domain. The values correspond to the consecutive topology levels, from
-	// the highest to the lowest.
-	//
+// +kubebuilder:validation:XValidation:rule="!has(self.podCounts.individual) || size(self.podCounts.individual) == self.domainCount", message="podCounts.individual must have length equal to domainCount"
+// +kubebuilder:validation:XValidation:rule="self.valuesPerLevel.all(x, !has(x.individual) || size(x.individual.roots) == self.domainCount)", message="valuesPerLevel.individual, if set, must have roots of length equal to domainCount of this TopologyAssignmentSlice"
+type TopologyAssignmentSlice struct {
+	// domainCount is the number of domains covered by this slice.
+	// +required
+	// +kubebuilder:validation:Minimum=1
+	DomainCount int32 `json:"domainCount,omitempty"`
+
+	// valuesPerLevel has one entry for each of the Levels specified in the TopologyAssignment.
+	// The entry corresponding to a particular level specifies the placement of pods at that level.
 	// +required
 	// +listType=atomic
 	// +kubebuilder:validation:MinItems=1
 	// +kubebuilder:validation:MaxItems=16
-	// +kubebuilder:validation:items:MaxLength=63
-	Values []string `json:"values,omitempty"`
+	ValuesPerLevel []TopologyAssignmentSliceLevelValues `json:"valuesPerLevel,omitempty"`
 
-	// count indicates the number of Pods to be scheduled in the topology
-	// domain indicated by the values field.
-	//
+	// podCounts specifies the number of pods allocated per each domain.
+	// +required
+	PodCounts TopologyAssignmentSlicePodCounts `json:"podCounts,omitempty"`
+}
+
+// +kubebuilder:validation:ExactlyOneOf=universal;individual
+type TopologyAssignmentSliceLevelValues struct {
+	// universal - if set - specifies a single topology placement value (at a particular topology level)
+	// that applies to all pods in the current TopologyAssignmentSlice.
+	// Exactly one of universal, individual must be set.
+	// +optional
+	// +kubebuilder:validation:MaxLength=63
+	Universal *string `json:"universal,omitempty"`
+
+	// individual - if set - specifies multiple topology placement values (at a particular topology level)
+	// that apply to the pods in the current TopologyAssignmentSlice.
+	// Exactly one of universal, individual must be set.
+	// +optional
+	Individual *TopologyAssignmentSliceLevelIndividualValues `json:"individual,omitempty"`
+}
+
+type TopologyAssignmentSliceLevelIndividualValues struct {
+	// commonPrefix specifies a common prefix for all values in this slice assignment.
+	// It must be either nil pointer or a non-empty string.
+	// +optional
+	// +kubebuilder:validation:MaxLength=63
+	CommonPrefix *string `json:"commonPrefix,omitempty"`
+	// commonSuffix specifies a common suffix for all values in this slice assignment.
+	// It must be either nil pointer or a non-empty string.
+	// +optional
+	// +kubebuilder:validation:MaxLength=63
+	CommonSuffix *string `json:"commonSuffix,omitempty"`
+
+	// roots specifies the values in this assignment (excluding commonPrefix and commonSuffix, if non-empty).
+	// Its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
 	// +required
+	// +listType=atomic
+	// +kubebuilder:validation:MinItems=1
+	// +kubebuilder:validation:MaxItems=100000
+	// +kubebuilder:validation:items:MaxLength=63
+	Roots []string `json:"roots,omitempty"`
+}
+
+// +kubebuilder:validation:ExactlyOneOf=universal;individual
+type TopologyAssignmentSlicePodCounts struct {
+	// universal - if set - specifies the number of pods allocated in every domain in this slice.
+	// Exactly one of universal, individual must be set.
+	// +optional
 	// +kubebuilder:validation:Minimum=1
-	Count int32 `json:"count,omitempty"`
+	Universal *int32 `json:"universal,omitempty"`
+
+	// individual - if set - specifies the number of pods allocated in each domain in this slice.
+	// If set, its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+	// Exactly one of universal, individual must be set.
+	// +optional
+	// +listType=atomic
+	// +kubebuilder:validation:MinItems=1
+	// +kubebuilder:validation:MaxItems=100000
+	// +kubebuilder:validation:items:Minimum=1
+	Individual []int32 `json:"individual,omitempty"`
 }
 
 // +kubebuilder:validation:XValidation:rule="has(self.minCount) ? self.minCount <= self.count : true", message="minCount should be positive and less or equal to count"
diff --git a/apis/kueue/v1beta2/zz_generated.deepcopy.go b/apis/kueue/v1beta2/zz_generated.deepcopy.go
index 77a57cd69..9b82dc8b6 100644
--- a/apis/kueue/v1beta2/zz_generated.deepcopy.go
+++ b/apis/kueue/v1beta2/zz_generated.deepcopy.go
@@ -1659,9 +1659,9 @@ func (in *TopologyAssignment) DeepCopyInto(out *TopologyAssignment) {
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
-	if in.Domains != nil {
-		in, out := &in.Domains, &out.Domains
-		*out = make([]TopologyDomainAssignment, len(*in))
+	if in.Slices != nil {
+		in, out := &in.Slices, &out.Slices
+		*out = make([]TopologyAssignmentSlice, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -1679,21 +1679,104 @@ func (in *TopologyAssignment) DeepCopy() *TopologyAssignment {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *TopologyDomainAssignment) DeepCopyInto(out *TopologyDomainAssignment) {
+func (in *TopologyAssignmentSlice) DeepCopyInto(out *TopologyAssignmentSlice) {
 	*out = *in
-	if in.Values != nil {
-		in, out := &in.Values, &out.Values
+	if in.ValuesPerLevel != nil {
+		in, out := &in.ValuesPerLevel, &out.ValuesPerLevel
+		*out = make([]TopologyAssignmentSliceLevelValues, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	in.PodCounts.DeepCopyInto(&out.PodCounts)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyAssignmentSlice.
+func (in *TopologyAssignmentSlice) DeepCopy() *TopologyAssignmentSlice {
+	if in == nil {
+		return nil
+	}
+	out := new(TopologyAssignmentSlice)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TopologyAssignmentSliceLevelIndividualValues) DeepCopyInto(out *TopologyAssignmentSliceLevelIndividualValues) {
+	*out = *in
+	if in.CommonPrefix != nil {
+		in, out := &in.CommonPrefix, &out.CommonPrefix
+		*out = new(string)
+		**out = **in
+	}
+	if in.CommonSuffix != nil {
+		in, out := &in.CommonSuffix, &out.CommonSuffix
+		*out = new(string)
+		**out = **in
+	}
+	if in.Roots != nil {
+		in, out := &in.Roots, &out.Roots
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyDomainAssignment.
-func (in *TopologyDomainAssignment) DeepCopy() *TopologyDomainAssignment {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyAssignmentSliceLevelIndividualValues.
+func (in *TopologyAssignmentSliceLevelIndividualValues) DeepCopy() *TopologyAssignmentSliceLevelIndividualValues {
+	if in == nil {
+		return nil
+	}
+	out := new(TopologyAssignmentSliceLevelIndividualValues)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TopologyAssignmentSliceLevelValues) DeepCopyInto(out *TopologyAssignmentSliceLevelValues) {
+	*out = *in
+	if in.Universal != nil {
+		in, out := &in.Universal, &out.Universal
+		*out = new(string)
+		**out = **in
+	}
+	if in.Individual != nil {
+		in, out := &in.Individual, &out.Individual
+		*out = new(TopologyAssignmentSliceLevelIndividualValues)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyAssignmentSliceLevelValues.
+func (in *TopologyAssignmentSliceLevelValues) DeepCopy() *TopologyAssignmentSliceLevelValues {
+	if in == nil {
+		return nil
+	}
+	out := new(TopologyAssignmentSliceLevelValues)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TopologyAssignmentSlicePodCounts) DeepCopyInto(out *TopologyAssignmentSlicePodCounts) {
+	*out = *in
+	if in.Universal != nil {
+		in, out := &in.Universal, &out.Universal
+		*out = new(int32)
+		**out = **in
+	}
+	if in.Individual != nil {
+		in, out := &in.Individual, &out.Individual
+		*out = make([]int32, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyAssignmentSlicePodCounts.
+func (in *TopologyAssignmentSlicePodCounts) DeepCopy() *TopologyAssignmentSlicePodCounts {
 	if in == nil {
 		return nil
 	}
-	out := new(TopologyDomainAssignment)
+	out := new(TopologyAssignmentSlicePodCounts)
 	in.DeepCopyInto(out)
 	return out
 }
diff --git a/charts/kueue/templates/crd/kueue.x-k8s.io_workloads.yaml b/charts/kueue/templates/crd/kueue.x-k8s.io_workloads.yaml
index aba263826..97ebb2267 100644
--- a/charts/kueue/templates/crd/kueue.x-k8s.io_workloads.yaml
+++ b/charts/kueue/templates/crd/kueue.x-k8s.io_workloads.yaml
@@ -17251,100 +17251,131 @@ spec:
                               This field will not change in case of quota reclaim.
                             type: object
                           topologyAssignment:
-                            description: |-
-                              topologyAssignment indicates the topology assignment divided into
-                              topology domains corresponding to the lowest level of the topology.
-                              The assignment specifies the number of Pods to be scheduled per topology
-                              domain and specifies the node selectors for each topology domain, in the
-                              following way: the node selector keys are specified by the levels field
-                              (same for all domains), and the corresponding node selector value is
-                              specified by the domains.values subfield. If the TopologySpec.Levels field contains
-                              "kubernetes.io/hostname" label, topologyAssignment will contain data only for
-                              this label, and omit higher levels in the topology
-
-                              Example:
-
-                              topologyAssignment:
-                                levels:
-                                - cloud.provider.com/topology-block
-                                - cloud.provider.com/topology-rack
-                                domains:
-                                - values: [block-1, rack-1]
-                                  count: 4
-                                - values: [block-1, rack-2]
-                                  count: 2
-
-                              Here:
-                              - 4 Pods are to be scheduled on nodes matching the node selector:
-                                cloud.provider.com/topology-block: block-1
-                                cloud.provider.com/topology-rack: rack-1
-                              - 2 Pods are to be scheduled on nodes matching the node selector:
-                                cloud.provider.com/topology-block: block-1
-                                cloud.provider.com/topology-rack: rack-2
-
-                              Example:
-                              Below there is an equivalent of the above example assuming, Topology
-                              object defines kubernetes.io/hostname as the lowest level in topology.
-                              Hence we omit higher level of topologies, since the hostname label
-                              is sufficient to explicitly identify a proper node.
-
-                              topologyAssignment:
-                                levels:
-                                - kubernetes.io/hostname
-                                domains:
-                                - values: [hostname-1]
-                                  count: 4
-                                - values: [hostname-2]
-                                  count: 2
+                            description: "topologyAssignment indicates the topology assignment divided into\ntopology domains corresponding to the lowest level of the topology.\nThe assignment specifies the number of Pods to be scheduled per topology\ndomain and specifies the node selectors for each topology domain, in the\nfollowing way:\n* `levels` specifies the node selector keys (same for all domains).\n  - If the TopologySpec.Levels field contains \"kubernetes.io/hostname\" label,\n    topologyAssignment will contain data only for this label,\n    and omit higher levels in the topology.\n* `slices` specifies the node selector values and pod counts for all domains\n  (which may be partitioned into separate slices).\n  - The node selector values are arranged first by topology level, only then by domain.\n    (This allows \"optimizing\" similar values; see below).\n* The format of `slices` supports the following variations\n  (aimed to optimize the total bytesize for very large number of domains; see examples below):\n  - When all node selector values (at a given topology level, in a given slice)\n    share a common prefix and/or suffix, these may be stored\n    in dedicated `commonPrefix`/`commonSuffix` fields.\n    If so, the array of `roots` will only store the remaining parts of these strings.\n  - When all node selector values (at a given topology level, in a given slice)\n    are identical, this may be represented by `universal` value.\n  - When all pod counts (in a given slice) are identical,\n    this may be represented by `universal` pod count.\n\nExample 1:\n\nThe following represents an assignment in which:\n* 4 Pods are to be scheduled on nodes matching the node selector:\n  - cloud.provider.com/topology-block: block-1\n  - cloud.provider.com/topology-rack: rack-1\n* 2 Pods are to be scheduled on nodes matching the node selector:\n  - cloud.provider.com/topology-block: block-1\n  - cloud.provider.com/topology-rack: rack-2\n\ntopologyAssignment:\n  levels:\n  - cloud.provider.com/topology-block\n  - cloud.provider.com/topology-rack\n  slices:\n  - domainCount: 2\n    valuesPerLevel:\n    - individual:\n        roots: [block-1, block-1]\n    - individual:\n        roots: [rack-1, rack-2]\n    podCounts:\n      individual: [4, 2]\n\nExample 2:\n\nThe following is equivalent to Example 1 - but using extracted prefix and universalValue.\n\ntopologyAssignment:\n  levels:\n  - cloud.provider.com/topology-block\n  - cloud.provider.com/topology-rack\n  slices:\n  - domainCount: 2\n    valuesPerLevel:\n    - universal: block-1\n    - individual:\n        prefix: rack-\n\t\t   roots: [1, 2]\n    podCounts:\n      individual: [4, 2]\n\nExample 3:\n\nNow suppose that:\n- the Topology object defines kubernetes.io/hostname as the lowest level\n  (and hence, in the topologyAssignment, we omit all other levels\n  since the hostname label suffices to explicitly identify a proper node),\n- we assign 1 Pod per each node,\n- the node naming scheme is `block-{blockId}-rack-{rackId}-node-{nodeId}`.\nThen, using the \"extraction of commons\", the assignment from Examples 1-2 would look as follows:\n\ntopologyAssignment:\n  levels:\n  - kubernetes.io/hostname\n  slices:\n  - domainCount: 6\n    valuesPerLevel:\n    - individual:\n        prefix: block-1-rack-\n\t\t   roots: [1-node-1, 1-node-2, 1-node-3, 1-node-4, 2-node-1, 2-node-2]\n    podCounts:\n      universal: 1\n\nExample 4:\n\nBy using multiple slices, we can afford even longer common prefixes.\nThe assignment from Example 3 can be alternatively represented as follows:\n\ntopologyAssignment:\n  levels:\n  - kubernetes.io/hostname\n  slices:\n  - domainCount: 4\n    valuesPerLevel:\n    - individual:\n        prefix: block-1-rack-1-node-\n\t\t   roots: [1, 2, 3, 4]\n    podCounts:\n      universal: 1\n  - domainCount: 2\n    valuesPerLevel:\n    - individual:\n        prefix: block-1-rack-2-node-\n\t\t   roots: [1, 2]\n    podCounts:\n      universal: 1"
                             properties:
-                              domains:
+                              levels:
                                 description: |-
-                                  domains is a list of topology assignments split by topology domains at
-                                  the lowest level of the topology.
+                                  levels is an ordered list of keys denoting the levels of the assigned
+                                  topology (i.e. node label keys), from the highest to the lowest level of
+                                  the topology.
+                                items:
+                                  maxLength: 317
+                                  type: string
+                                maxItems: 16
+                                minItems: 1
+                                type: array
+                                x-kubernetes-list-type: atomic
+                              slices:
+                                description: |-
+                                  slices represent topology assignments for subsets of pods of a workload.
+                                  The full assignment is obtained as a union of all slices.
                                 items:
                                   properties:
-                                    count:
-                                      description: |-
-                                        count indicates the number of Pods to be scheduled in the topology
-                                        domain indicated by the values field.
+                                    domainCount:
+                                      description: domainCount is the number of domains covered by this slice.
                                       format: int32
                                       minimum: 1
                                       type: integer
-                                    values:
+                                    podCounts:
+                                      description: podCounts specifies the number of pods allocated per each domain.
+                                      properties:
+                                        individual:
+                                          description: |-
+                                            individual - if set - specifies the number of pods allocated in each domain in this slice.
+                                            If set, its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+                                            Exactly one of universal, individual must be set.
+                                          items:
+                                            format: int32
+                                            minimum: 1
+                                            type: integer
+                                          maxItems: 100000
+                                          minItems: 1
+                                          type: array
+                                          x-kubernetes-list-type: atomic
+                                        universal:
+                                          description: |-
+                                            universal - if set - specifies the number of pods allocated in every domain in this slice.
+                                            Exactly one of universal, individual must be set.
+                                          format: int32
+                                          minimum: 1
+                                          type: integer
+                                      type: object
+                                      x-kubernetes-validations:
+                                        - message: exactly one of the fields in [universal individual] must be set
+                                          rule: '[has(self.universal),has(self.individual)].filter(x,x==true).size() == 1'
+                                    valuesPerLevel:
                                       description: |-
-                                        values is an ordered list of node selector values describing a topology
-                                        domain. The values correspond to the consecutive topology levels, from
-                                        the highest to the lowest.
+                                        valuesPerLevel has one entry for each of the Levels specified in the TopologyAssignment.
+                                        The entry corresponding to a particular level specifies the placement of pods at that level.
                                       items:
-                                        maxLength: 63
-                                        type: string
+                                        properties:
+                                          individual:
+                                            description: |-
+                                              individual - if set - specifies multiple topology placement values (at a particular topology level)
+                                              that apply to the pods in the current TopologyAssignmentSlice.
+                                              Exactly one of universal, individual must be set.
+                                            properties:
+                                              commonPrefix:
+                                                description: |-
+                                                  commonPrefix specifies a common prefix for all values in this slice assignment.
+                                                  It must be either nil pointer or a non-empty string.
+                                                maxLength: 63
+                                                type: string
+                                              commonSuffix:
+                                                description: |-
+                                                  commonSuffix specifies a common suffix for all values in this slice assignment.
+                                                  It must be either nil pointer or a non-empty string.
+                                                maxLength: 63
+                                                type: string
+                                              roots:
+                                                description: |-
+                                                  roots specifies the values in this assignment (excluding commonPrefix and commonSuffix, if non-empty).
+                                                  Its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+                                                items:
+                                                  maxLength: 63
+                                                  type: string
+                                                maxItems: 100000
+                                                minItems: 1
+                                                type: array
+                                                x-kubernetes-list-type: atomic
+                                            required:
+                                              - roots
+                                            type: object
+                                          universal:
+                                            description: |-
+                                              universal - if set - specifies a single topology placement value (at a particular topology level)
+                                              that applies to all pods in the current TopologyAssignmentSlice.
+                                              Exactly one of universal, individual must be set.
+                                            maxLength: 63
+                                            type: string
+                                        type: object
+                                        x-kubernetes-validations:
+                                          - message: exactly one of the fields in [universal individual] must be set
+                                            rule: '[has(self.universal),has(self.individual)].filter(x,x==true).size() == 1'
                                       maxItems: 16
                                       minItems: 1
                                       type: array
                                       x-kubernetes-list-type: atomic
                                   required:
-                                    - count
-                                    - values
+                                    - domainCount
+                                    - podCounts
+                                    - valuesPerLevel
                                   type: object
-                                maxItems: 100000
-                                type: array
-                                x-kubernetes-list-type: atomic
-                              levels:
-                                description: |-
-                                  levels is an ordered list of keys denoting the levels of the assigned
-                                  topology (i.e. node label keys), from the highest to the lowest level of
-                                  the topology.
-                                items:
-                                  maxLength: 317
-                                  type: string
-                                maxItems: 16
-                                minItems: 1
+                                  x-kubernetes-validations:
+                                    - message: podCounts.individual must have length equal to domainCount
+                                      rule: '!has(self.podCounts.individual) || size(self.podCounts.individual) == self.domainCount'
+                                    - message: valuesPerLevel.individual, if set, must have roots of length equal to domainCount of this TopologyAssignmentSlice
+                                      rule: self.valuesPerLevel.all(x, !has(x.individual) || size(x.individual.roots) == self.domainCount)
+                                maxItems: 1000
                                 type: array
                                 x-kubernetes-list-type: atomic
                             required:
-                              - domains
                               - levels
+                              - slices
                             type: object
+                            x-kubernetes-validations:
+                              - message: valuesPerLevel must have the same length as the number of levels in this TopologyAssignment
+                                rule: self.slices.all(x, size(x.valuesPerLevel) == size(self.levels))
                         type: object
                       maxItems: 8
                       type: array
diff --git a/charts/kueue/values.yaml b/charts/kueue/values.yaml
index dac543d6a..7d756560e 100644
--- a/charts/kueue/values.yaml
+++ b/charts/kueue/values.yaml
@@ -160,7 +160,6 @@ managerConfig:
     #  externalFrameworks:
     #  - "Foo.v1.example.com"
     #fairSharing:
-    #  enable: true
     #  preemptionStrategies: [LessThanOrEqualToFinalShare, LessThanInitialShare]
     #admissionFairSharing:
     #  usageHalfLifeTime: "168h" # 7 days
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologyassignment.go b/client-go/applyconfiguration/kueue/v1beta2/topologyassignment.go
index 969bbd294..226b0ba01 100644
--- a/client-go/applyconfiguration/kueue/v1beta2/topologyassignment.go
+++ b/client-go/applyconfiguration/kueue/v1beta2/topologyassignment.go
@@ -20,8 +20,8 @@ package v1beta2
 // TopologyAssignmentApplyConfiguration represents a declarative configuration of the TopologyAssignment type for use
 // with apply.
 type TopologyAssignmentApplyConfiguration struct {
-	Levels  []string                                     `json:"levels,omitempty"`
-	Domains []TopologyDomainAssignmentApplyConfiguration `json:"domains,omitempty"`
+	Levels []string                                    `json:"levels,omitempty"`
+	Slices []TopologyAssignmentSliceApplyConfiguration `json:"slices,omitempty"`
 }
 
 // TopologyAssignmentApplyConfiguration constructs a declarative configuration of the TopologyAssignment type for use with
@@ -40,15 +40,15 @@ func (b *TopologyAssignmentApplyConfiguration) WithLevels(values ...string) *Top
 	return b
 }
 
-// WithDomains adds the given value to the Domains field in the declarative configuration
+// WithSlices adds the given value to the Slices 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 Domains field.
-func (b *TopologyAssignmentApplyConfiguration) WithDomains(values ...*TopologyDomainAssignmentApplyConfiguration) *TopologyAssignmentApplyConfiguration {
+// If called multiple times, values provided by each call will be appended to the Slices field.
+func (b *TopologyAssignmentApplyConfiguration) WithSlices(values ...*TopologyAssignmentSliceApplyConfiguration) *TopologyAssignmentApplyConfiguration {
 	for i := range values {
 		if values[i] == nil {
-			panic("nil value passed to WithDomains")
+			panic("nil value passed to WithSlices")
 		}
-		b.Domains = append(b.Domains, *values[i])
+		b.Slices = append(b.Slices, *values[i])
 	}
 	return b
 }
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslice.go b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslice.go
new file mode 100644
index 000000000..cc065891d
--- /dev/null
+++ b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslice.go
@@ -0,0 +1,61 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1beta2
+
+// TopologyAssignmentSliceApplyConfiguration represents a declarative configuration of the TopologyAssignmentSlice type for use
+// with apply.
+type TopologyAssignmentSliceApplyConfiguration struct {
+	DomainCount    *int32                                                 `json:"domainCount,omitempty"`
+	ValuesPerLevel []TopologyAssignmentSliceLevelValuesApplyConfiguration `json:"valuesPerLevel,omitempty"`
+	PodCounts      *TopologyAssignmentSlicePodCountsApplyConfiguration    `json:"podCounts,omitempty"`
+}
+
+// TopologyAssignmentSliceApplyConfiguration constructs a declarative configuration of the TopologyAssignmentSlice type for use with
+// apply.
+func TopologyAssignmentSlice() *TopologyAssignmentSliceApplyConfiguration {
+	return &TopologyAssignmentSliceApplyConfiguration{}
+}
+
+// WithDomainCount sets the DomainCount field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the DomainCount field is set to the value of the last call.
+func (b *TopologyAssignmentSliceApplyConfiguration) WithDomainCount(value int32) *TopologyAssignmentSliceApplyConfiguration {
+	b.DomainCount = &value
+	return b
+}
+
+// WithValuesPerLevel adds the given value to the ValuesPerLevel 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 ValuesPerLevel field.
+func (b *TopologyAssignmentSliceApplyConfiguration) WithValuesPerLevel(values ...*TopologyAssignmentSliceLevelValuesApplyConfiguration) *TopologyAssignmentSliceApplyConfiguration {
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithValuesPerLevel")
+		}
+		b.ValuesPerLevel = append(b.ValuesPerLevel, *values[i])
+	}
+	return b
+}
+
+// WithPodCounts sets the PodCounts field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the PodCounts field is set to the value of the last call.
+func (b *TopologyAssignmentSliceApplyConfiguration) WithPodCounts(value *TopologyAssignmentSlicePodCountsApplyConfiguration) *TopologyAssignmentSliceApplyConfiguration {
+	b.PodCounts = value
+	return b
+}
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelindividualvalues.go b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelindividualvalues.go
new file mode 100644
index 000000000..ce4789e0a
--- /dev/null
+++ b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelindividualvalues.go
@@ -0,0 +1,58 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1beta2
+
+// TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration represents a declarative configuration of the TopologyAssignmentSliceLevelIndividualValues type for use
+// with apply.
+type TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration struct {
+	CommonPrefix *string  `json:"commonPrefix,omitempty"`
+	CommonSuffix *string  `json:"commonSuffix,omitempty"`
+	Roots        []string `json:"roots,omitempty"`
+}
+
+// TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration constructs a declarative configuration of the TopologyAssignmentSliceLevelIndividualValues type for use with
+// apply.
+func TopologyAssignmentSliceLevelIndividualValues() *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration {
+	return &TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration{}
+}
+
+// WithCommonPrefix sets the CommonPrefix field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the CommonPrefix field is set to the value of the last call.
+func (b *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration) WithCommonPrefix(value string) *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration {
+	b.CommonPrefix = &value
+	return b
+}
+
+// WithCommonSuffix sets the CommonSuffix field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the CommonSuffix field is set to the value of the last call.
+func (b *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration) WithCommonSuffix(value string) *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration {
+	b.CommonSuffix = &value
+	return b
+}
+
+// WithRoots adds the given value to the Roots 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 Roots field.
+func (b *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration) WithRoots(values ...string) *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration {
+	for i := range values {
+		b.Roots = append(b.Roots, values[i])
+	}
+	return b
+}
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelvalues.go b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelvalues.go
new file mode 100644
index 000000000..2af4ee272
--- /dev/null
+++ b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicelevelvalues.go
@@ -0,0 +1,47 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1beta2
+
+// TopologyAssignmentSliceLevelValuesApplyConfiguration represents a declarative configuration of the TopologyAssignmentSliceLevelValues type for use
+// with apply.
+type TopologyAssignmentSliceLevelValuesApplyConfiguration struct {
+	Universal  *string                                                         `json:"universal,omitempty"`
+	Individual *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration `json:"individual,omitempty"`
+}
+
+// TopologyAssignmentSliceLevelValuesApplyConfiguration constructs a declarative configuration of the TopologyAssignmentSliceLevelValues type for use with
+// apply.
+func TopologyAssignmentSliceLevelValues() *TopologyAssignmentSliceLevelValuesApplyConfiguration {
+	return &TopologyAssignmentSliceLevelValuesApplyConfiguration{}
+}
+
+// WithUniversal sets the Universal field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Universal field is set to the value of the last call.
+func (b *TopologyAssignmentSliceLevelValuesApplyConfiguration) WithUniversal(value string) *TopologyAssignmentSliceLevelValuesApplyConfiguration {
+	b.Universal = &value
+	return b
+}
+
+// WithIndividual sets the Individual field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Individual field is set to the value of the last call.
+func (b *TopologyAssignmentSliceLevelValuesApplyConfiguration) WithIndividual(value *TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration) *TopologyAssignmentSliceLevelValuesApplyConfiguration {
+	b.Individual = value
+	return b
+}
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicepodcounts.go b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicepodcounts.go
new file mode 100644
index 000000000..f46ab96a6
--- /dev/null
+++ b/client-go/applyconfiguration/kueue/v1beta2/topologyassignmentslicepodcounts.go
@@ -0,0 +1,49 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1beta2
+
+// TopologyAssignmentSlicePodCountsApplyConfiguration represents a declarative configuration of the TopologyAssignmentSlicePodCounts type for use
+// with apply.
+type TopologyAssignmentSlicePodCountsApplyConfiguration struct {
+	Universal  *int32  `json:"universal,omitempty"`
+	Individual []int32 `json:"individual,omitempty"`
+}
+
+// TopologyAssignmentSlicePodCountsApplyConfiguration constructs a declarative configuration of the TopologyAssignmentSlicePodCounts type for use with
+// apply.
+func TopologyAssignmentSlicePodCounts() *TopologyAssignmentSlicePodCountsApplyConfiguration {
+	return &TopologyAssignmentSlicePodCountsApplyConfiguration{}
+}
+
+// WithUniversal sets the Universal field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Universal field is set to the value of the last call.
+func (b *TopologyAssignmentSlicePodCountsApplyConfiguration) WithUniversal(value int32) *TopologyAssignmentSlicePodCountsApplyConfiguration {
+	b.Universal = &value
+	return b
+}
+
+// WithIndividual adds the given value to the Individual 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 Individual field.
+func (b *TopologyAssignmentSlicePodCountsApplyConfiguration) WithIndividual(values ...int32) *TopologyAssignmentSlicePodCountsApplyConfiguration {
+	for i := range values {
+		b.Individual = append(b.Individual, values[i])
+	}
+	return b
+}
diff --git a/client-go/applyconfiguration/kueue/v1beta2/topologydomainassignment.go b/client-go/applyconfiguration/kueue/v1beta2/topologydomainassignment.go
deleted file mode 100644
index 32d97305f..000000000
--- a/client-go/applyconfiguration/kueue/v1beta2/topologydomainassignment.go
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-Copyright The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// Code generated by applyconfiguration-gen. DO NOT EDIT.
-
-package v1beta2
-
-// TopologyDomainAssignmentApplyConfiguration represents a declarative configuration of the TopologyDomainAssignment type for use
-// with apply.
-type TopologyDomainAssignmentApplyConfiguration struct {
-	Values []string `json:"values,omitempty"`
-	Count  *int32   `json:"count,omitempty"`
-}
-
-// TopologyDomainAssignmentApplyConfiguration constructs a declarative configuration of the TopologyDomainAssignment type for use with
-// apply.
-func TopologyDomainAssignment() *TopologyDomainAssignmentApplyConfiguration {
-	return &TopologyDomainAssignmentApplyConfiguration{}
-}
-
-// WithValues adds the given value to the Values 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 Values field.
-func (b *TopologyDomainAssignmentApplyConfiguration) WithValues(values ...string) *TopologyDomainAssignmentApplyConfiguration {
-	for i := range values {
-		b.Values = append(b.Values, values[i])
-	}
-	return b
-}
-
-// WithCount sets the Count field in the declarative configuration to the given value
-// and returns the receiver, so that objects can be built by chaining "With" function invocations.
-// If called multiple times, the Count field is set to the value of the last call.
-func (b *TopologyDomainAssignmentApplyConfiguration) WithCount(value int32) *TopologyDomainAssignmentApplyConfiguration {
-	b.Count = &value
-	return b
-}
diff --git a/client-go/applyconfiguration/utils.go b/client-go/applyconfiguration/utils.go
index 162c4b523..4a50fa25e 100644
--- a/client-go/applyconfiguration/utils.go
+++ b/client-go/applyconfiguration/utils.go
@@ -283,8 +283,14 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
 		return &kueuev1beta2.TopologyApplyConfiguration{}
 	case v1beta2.SchemeGroupVersion.WithKind("TopologyAssignment"):
 		return &kueuev1beta2.TopologyAssignmentApplyConfiguration{}
-	case v1beta2.SchemeGroupVersion.WithKind("TopologyDomainAssignment"):
-		return &kueuev1beta2.TopologyDomainAssignmentApplyConfiguration{}
+	case v1beta2.SchemeGroupVersion.WithKind("TopologyAssignmentSlice"):
+		return &kueuev1beta2.TopologyAssignmentSliceApplyConfiguration{}
+	case v1beta2.SchemeGroupVersion.WithKind("TopologyAssignmentSliceLevelIndividualValues"):
+		return &kueuev1beta2.TopologyAssignmentSliceLevelIndividualValuesApplyConfiguration{}
+	case v1beta2.SchemeGroupVersion.WithKind("TopologyAssignmentSliceLevelValues"):
+		return &kueuev1beta2.TopologyAssignmentSliceLevelValuesApplyConfiguration{}
+	case v1beta2.SchemeGroupVersion.WithKind("TopologyAssignmentSlicePodCounts"):
+		return &kueuev1beta2.TopologyAssignmentSlicePodCountsApplyConfiguration{}
 	case v1beta2.SchemeGroupVersion.WithKind("TopologyLevel"):
 		return &kueuev1beta2.TopologyLevelApplyConfiguration{}
 	case v1beta2.SchemeGroupVersion.WithKind("TopologySpec"):
diff --git a/cmd/kueue/main.go b/cmd/kueue/main.go
index 90cc6a2a8..6cb775687 100644
--- a/cmd/kueue/main.go
+++ b/cmd/kueue/main.go
@@ -70,6 +70,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/metrics"
 	"sigs.k8s.io/kueue/pkg/scheduler"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/fairsharing"
 	"sigs.k8s.io/kueue/pkg/util/cert"
 	"sigs.k8s.io/kueue/pkg/util/kubeversion"
 	"sigs.k8s.io/kueue/pkg/util/useragent"
@@ -234,7 +235,7 @@ func main() {
 		setupLog.Info("DRA mapper initialized from configuration")
 	}
 	if cfg.FairSharing != nil {
-		cacheOptions = append(cacheOptions, schdcache.WithFairSharing(cfg.FairSharing.Enable))
+		cacheOptions = append(cacheOptions, schdcache.WithFairSharing(fairsharing.Enabled(cfg.FairSharing)))
 	}
 	if cfg.AdmissionFairSharing != nil {
 		queueOptions = append(queueOptions, qcache.WithAdmissionFairSharing(cfg.AdmissionFairSharing))
diff --git a/config/components/crd/bases/kueue.x-k8s.io_workloads.yaml b/config/components/crd/bases/kueue.x-k8s.io_workloads.yaml
index 5dfd7860f..2d4a9b04f 100644
--- a/config/components/crd/bases/kueue.x-k8s.io_workloads.yaml
+++ b/config/components/crd/bases/kueue.x-k8s.io_workloads.yaml
@@ -18250,100 +18250,209 @@ spec:
                             This field will not change in case of quota reclaim.
                           type: object
                         topologyAssignment:
-                          description: |-
-                            topologyAssignment indicates the topology assignment divided into
-                            topology domains corresponding to the lowest level of the topology.
-                            The assignment specifies the number of Pods to be scheduled per topology
-                            domain and specifies the node selectors for each topology domain, in the
-                            following way: the node selector keys are specified by the levels field
-                            (same for all domains), and the corresponding node selector value is
-                            specified by the domains.values subfield. If the TopologySpec.Levels field contains
-                            "kubernetes.io/hostname" label, topologyAssignment will contain data only for
-                            this label, and omit higher levels in the topology
-
-                            Example:
-
-                            topologyAssignment:
-                              levels:
-                              - cloud.provider.com/topology-block
-                              - cloud.provider.com/topology-rack
-                              domains:
-                              - values: [block-1, rack-1]
-                                count: 4
-                              - values: [block-1, rack-2]
-                                count: 2
-
-                            Here:
-                            - 4 Pods are to be scheduled on nodes matching the node selector:
-                              cloud.provider.com/topology-block: block-1
-                              cloud.provider.com/topology-rack: rack-1
-                            - 2 Pods are to be scheduled on nodes matching the node selector:
-                              cloud.provider.com/topology-block: block-1
-                              cloud.provider.com/topology-rack: rack-2
-
-                            Example:
-                            Below there is an equivalent of the above example assuming, Topology
-                            object defines kubernetes.io/hostname as the lowest level in topology.
-                            Hence we omit higher level of topologies, since the hostname label
-                            is sufficient to explicitly identify a proper node.
-
-                            topologyAssignment:
-                              levels:
-                              - kubernetes.io/hostname
-                              domains:
-                              - values: [hostname-1]
-                                count: 4
-                              - values: [hostname-2]
-                                count: 2
+                          description: "topologyAssignment indicates the topology
+                            assignment divided into\ntopology domains corresponding
+                            to the lowest level of the topology.\nThe assignment specifies
+                            the number of Pods to be scheduled per topology\ndomain
+                            and specifies the node selectors for each topology domain,
+                            in the\nfollowing way:\n* `levels` specifies the node
+                            selector keys (same for all domains).\n  - If the TopologySpec.Levels
+                            field contains \"kubernetes.io/hostname\" label,\n    topologyAssignment
+                            will contain data only for this label,\n    and omit higher
+                            levels in the topology.\n* `slices` specifies the node
+                            selector values and pod counts for all domains\n  (which
+                            may be partitioned into separate slices).\n  - The node
+                            selector values are arranged first by topology level,
+                            only then by domain.\n    (This allows \"optimizing\"
+                            similar values; see below).\n* The format of `slices`
+                            supports the following variations\n  (aimed to optimize
+                            the total bytesize for very large number of domains; see
+                            examples below):\n  - When all node selector values (at
+                            a given topology level, in a given slice)\n    share a
+                            common prefix and/or suffix, these may be stored\n    in
+                            dedicated `commonPrefix`/`commonSuffix` fields.\n    If
+                            so, the array of `roots` will only store the remaining
+                            parts of these strings.\n  - When all node selector values
+                            (at a given topology level, in a given slice)\n    are
+                            identical, this may be represented by `universal` value.\n
+                            \ - When all pod counts (in a given slice) are identical,\n
+                            \   this may be represented by `universal` pod count.\n\nExample
+                            1:\n\nThe following represents an assignment in which:\n*
+                            4 Pods are to be scheduled on nodes matching the node
+                            selector:\n  - cloud.provider.com/topology-block: block-1\n
+                            \ - cloud.provider.com/topology-rack: rack-1\n* 2 Pods
+                            are to be scheduled on nodes matching the node selector:\n
+                            \ - cloud.provider.com/topology-block: block-1\n  - cloud.provider.com/topology-rack:
+                            rack-2\n\ntopologyAssignment:\n  levels:\n  - cloud.provider.com/topology-block\n
+                            \ - cloud.provider.com/topology-rack\n  slices:\n  - domainCount:
+                            2\n    valuesPerLevel:\n    - individual:\n        roots:
+                            [block-1, block-1]\n    - individual:\n        roots:
+                            [rack-1, rack-2]\n    podCounts:\n      individual: [4,
+                            2]\n\nExample 2:\n\nThe following is equivalent to Example
+                            1 - but using extracted prefix and universalValue.\n\ntopologyAssignment:\n
+                            \ levels:\n  - cloud.provider.com/topology-block\n  -
+                            cloud.provider.com/topology-rack\n  slices:\n  - domainCount:
+                            2\n    valuesPerLevel:\n    - universal: block-1\n    -
+                            individual:\n        prefix: rack-\n\t\t   roots: [1,
+                            2]\n    podCounts:\n      individual: [4, 2]\n\nExample
+                            3:\n\nNow suppose that:\n- the Topology object defines
+                            kubernetes.io/hostname as the lowest level\n  (and hence,
+                            in the topologyAssignment, we omit all other levels\n
+                            \ since the hostname label suffices to explicitly identify
+                            a proper node),\n- we assign 1 Pod per each node,\n- the
+                            node naming scheme is `block-{blockId}-rack-{rackId}-node-{nodeId}`.\nThen,
+                            using the \"extraction of commons\", the assignment from
+                            Examples 1-2 would look as follows:\n\ntopologyAssignment:\n
+                            \ levels:\n  - kubernetes.io/hostname\n  slices:\n  -
+                            domainCount: 6\n    valuesPerLevel:\n    - individual:\n
+                            \       prefix: block-1-rack-\n\t\t   roots: [1-node-1,
+                            1-node-2, 1-node-3, 1-node-4, 2-node-1, 2-node-2]\n    podCounts:\n
+                            \     universal: 1\n\nExample 4:\n\nBy using multiple
+                            slices, we can afford even longer common prefixes.\nThe
+                            assignment from Example 3 can be alternatively represented
+                            as follows:\n\ntopologyAssignment:\n  levels:\n  - kubernetes.io/hostname\n
+                            \ slices:\n  - domainCount: 4\n    valuesPerLevel:\n    -
+                            individual:\n        prefix: block-1-rack-1-node-\n\t\t
+                            \  roots: [1, 2, 3, 4]\n    podCounts:\n      universal:
+                            1\n  - domainCount: 2\n    valuesPerLevel:\n    - individual:\n
+                            \       prefix: block-1-rack-2-node-\n\t\t   roots: [1,
+                            2]\n    podCounts:\n      universal: 1"
                           properties:
-                            domains:
+                            levels:
                               description: |-
-                                domains is a list of topology assignments split by topology domains at
-                                the lowest level of the topology.
+                                levels is an ordered list of keys denoting the levels of the assigned
+                                topology (i.e. node label keys), from the highest to the lowest level of
+                                the topology.
+                              items:
+                                maxLength: 317
+                                type: string
+                              maxItems: 16
+                              minItems: 1
+                              type: array
+                              x-kubernetes-list-type: atomic
+                            slices:
+                              description: |-
+                                slices represent topology assignments for subsets of pods of a workload.
+                                The full assignment is obtained as a union of all slices.
                               items:
                                 properties:
-                                  count:
-                                    description: |-
-                                      count indicates the number of Pods to be scheduled in the topology
-                                      domain indicated by the values field.
+                                  domainCount:
+                                    description: domainCount is the number of domains
+                                      covered by this slice.
                                     format: int32
                                     minimum: 1
                                     type: integer
-                                  values:
+                                  podCounts:
+                                    description: podCounts specifies the number of
+                                      pods allocated per each domain.
+                                    properties:
+                                      individual:
+                                        description: |-
+                                          individual - if set - specifies the number of pods allocated in each domain in this slice.
+                                          If set, its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+                                          Exactly one of universal, individual must be set.
+                                        items:
+                                          format: int32
+                                          minimum: 1
+                                          type: integer
+                                        maxItems: 100000
+                                        minItems: 1
+                                        type: array
+                                        x-kubernetes-list-type: atomic
+                                      universal:
+                                        description: |-
+                                          universal - if set - specifies the number of pods allocated in every domain in this slice.
+                                          Exactly one of universal, individual must be set.
+                                        format: int32
+                                        minimum: 1
+                                        type: integer
+                                    type: object
+                                    x-kubernetes-validations:
+                                    - message: exactly one of the fields in [universal
+                                        individual] must be set
+                                      rule: '[has(self.universal),has(self.individual)].filter(x,x==true).size()
+                                        == 1'
+                                  valuesPerLevel:
                                     description: |-
-                                      values is an ordered list of node selector values describing a topology
-                                      domain. The values correspond to the consecutive topology levels, from
-                                      the highest to the lowest.
+                                      valuesPerLevel has one entry for each of the Levels specified in the TopologyAssignment.
+                                      The entry corresponding to a particular level specifies the placement of pods at that level.
                                     items:
-                                      maxLength: 63
-                                      type: string
+                                      properties:
+                                        individual:
+                                          description: |-
+                                            individual - if set - specifies multiple topology placement values (at a particular topology level)
+                                            that apply to the pods in the current TopologyAssignmentSlice.
+                                            Exactly one of universal, individual must be set.
+                                          properties:
+                                            commonPrefix:
+                                              description: |-
+                                                commonPrefix specifies a common prefix for all values in this slice assignment.
+                                                It must be either nil pointer or a non-empty string.
+                                              maxLength: 63
+                                              type: string
+                                            commonSuffix:
+                                              description: |-
+                                                commonSuffix specifies a common suffix for all values in this slice assignment.
+                                                It must be either nil pointer or a non-empty string.
+                                              maxLength: 63
+                                              type: string
+                                            roots:
+                                              description: |-
+                                                roots specifies the values in this assignment (excluding commonPrefix and commonSuffix, if non-empty).
+                                                Its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+                                              items:
+                                                maxLength: 63
+                                                type: string
+                                              maxItems: 100000
+                                              minItems: 1
+                                              type: array
+                                              x-kubernetes-list-type: atomic
+                                          required:
+                                          - roots
+                                          type: object
+                                        universal:
+                                          description: |-
+                                            universal - if set - specifies a single topology placement value (at a particular topology level)
+                                            that applies to all pods in the current TopologyAssignmentSlice.
+                                            Exactly one of universal, individual must be set.
+                                          maxLength: 63
+                                          type: string
+                                      type: object
+                                      x-kubernetes-validations:
+                                      - message: exactly one of the fields in [universal
+                                          individual] must be set
+                                        rule: '[has(self.universal),has(self.individual)].filter(x,x==true).size()
+                                          == 1'
                                     maxItems: 16
                                     minItems: 1
                                     type: array
                                     x-kubernetes-list-type: atomic
                                 required:
-                                - count
-                                - values
+                                - domainCount
+                                - podCounts
+                                - valuesPerLevel
                                 type: object
-                              maxItems: 100000
-                              type: array
-                              x-kubernetes-list-type: atomic
-                            levels:
-                              description: |-
-                                levels is an ordered list of keys denoting the levels of the assigned
-                                topology (i.e. node label keys), from the highest to the lowest level of
-                                the topology.
-                              items:
-                                maxLength: 317
-                                type: string
-                              maxItems: 16
-                              minItems: 1
+                                x-kubernetes-validations:
+                                - message: podCounts.individual must have length equal
+                                    to domainCount
+                                  rule: '!has(self.podCounts.individual) || size(self.podCounts.individual)
+                                    == self.domainCount'
+                                - message: valuesPerLevel.individual, if set, must
+                                    have roots of length equal to domainCount of this
+                                    TopologyAssignmentSlice
+                                  rule: self.valuesPerLevel.all(x, !has(x.individual)
+                                    || size(x.individual.roots) == self.domainCount)
+                              maxItems: 1000
                               type: array
                               x-kubernetes-list-type: atomic
                           required:
-                          - domains
                           - levels
+                          - slices
                           type: object
+                          x-kubernetes-validations:
+                          - message: valuesPerLevel must have the same length as the
+                              number of levels in this TopologyAssignment
+                            rule: self.slices.all(x, size(x.valuesPerLevel) == size(self.levels))
                       type: object
                     maxItems: 8
                     type: array
diff --git a/config/components/manager/controller_manager_config.yaml b/config/components/manager/controller_manager_config.yaml
index 5a1614fc8..ce0c6455a 100644
--- a/config/components/manager/controller_manager_config.yaml
+++ b/config/components/manager/controller_manager_config.yaml
@@ -64,7 +64,6 @@ integrations:
 #  externalFrameworks:
 #  - "Foo.v1.example.com"
 #fairSharing:
-#  enable: true
 #  preemptionStrategies: [LessThanOrEqualToFinalShare, LessThanInitialShare]
 #admissionFairSharing:
 #  usageHalfLifeTime: "168h" # 7 days
diff --git a/hack/internal/tools/go.mod b/hack/internal/tools/go.mod
index 80852c90d..6cc09aaf2 100644
--- a/hack/internal/tools/go.mod
+++ b/hack/internal/tools/go.mod
@@ -33,7 +33,7 @@ require (
 	gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
 	gopkg.in/yaml.v3 v3.0.1
 	gotest.tools/gotestsum v1.13.0
-	helm.sh/helm/v3 v3.19.0
+	helm.sh/helm/v4 v4.0.0
 	k8s.io/code-generator v0.34.1
 	sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250407161039-6ad5c1dd4418
 	sigs.k8s.io/controller-tools v0.19.0
@@ -88,6 +88,7 @@ require (
 	github.com/Masterminds/vcs v1.13.3 // indirect
 	github.com/MirrexOne/unqueryvet v1.2.1 // indirect
 	github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
+	github.com/ProtonMail/go-crypto v1.3.0 // indirect
 	github.com/a8m/envsubst v1.4.3 // indirect
 	github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a // indirect
 	github.com/alecthomas/chroma/v2 v2.20.0 // indirect
@@ -162,6 +163,7 @@ require (
 	github.com/charmbracelet/x/term v0.2.1 // indirect
 	github.com/ckaznocha/intrange v0.3.1 // indirect
 	github.com/clbanning/mxj/v2 v2.7.0 // indirect
+	github.com/cloudflare/circl v1.6.1 // indirect
 	github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
 	github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
 	github.com/containerd/containerd v1.7.29 // indirect
@@ -170,7 +172,7 @@ require (
 	github.com/containerd/platforms v0.2.1 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
 	github.com/curioswitch/go-reassign v0.3.0 // indirect
-	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
+	github.com/cyphar/filepath-securejoin v0.5.0 // indirect
 	github.com/daixiang0/gci v0.13.7 // indirect
 	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
 	github.com/dave/dst v0.27.3 // indirect
@@ -181,19 +183,22 @@ require (
 	github.com/dlclark/regexp2 v1.11.5 // indirect
 	github.com/dnephin/pflag v1.0.7 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
 	github.com/elliotchance/orderedmap v1.8.0 // indirect
 	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
 	github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
 	github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
 	github.com/ettle/strcase v0.2.0 // indirect
 	github.com/evanphx/json-patch v5.9.11+incompatible // indirect
-	github.com/evanphx/json-patch/v5 v5.6.0 // indirect
+	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
 	github.com/evanw/esbuild v0.25.3 // indirect
 	github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
+	github.com/extism/go-sdk v1.7.1 // indirect
 	github.com/fatih/color v1.18.0 // indirect
 	github.com/fatih/structtag v1.2.0 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/firefart/nonamedreturns v1.0.6 // indirect
+	github.com/fluxcd/cli-utils v0.36.0-flux.14 // indirect
 	github.com/frankban/quicktest v1.14.6 // indirect
 	github.com/fsnotify/fsnotify v1.9.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
@@ -202,16 +207,16 @@ require (
 	github.com/ghodss/yaml v1.0.0 // indirect
 	github.com/ghostiam/protogetter v0.3.16 // indirect
 	github.com/go-critic/go-critic v0.13.0 // indirect
-	github.com/go-errors/errors v1.4.2 // indirect
+	github.com/go-errors/errors v1.5.1 // indirect
 	github.com/go-gorp/gorp/v3 v3.1.0 // indirect
 	github.com/go-ini/ini v1.67.0 // indirect
 	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
 	github.com/go-logr/logr v1.4.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/zapr v1.3.0 // indirect
-	github.com/go-openapi/jsonpointer v0.21.0 // indirect
-	github.com/go-openapi/jsonreference v0.20.2 // indirect
-	github.com/go-openapi/swag v0.23.0 // indirect
+	github.com/go-openapi/jsonpointer v0.21.1 // indirect
+	github.com/go-openapi/jsonreference v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
 	github.com/go-toolsmith/astcast v1.1.0 // indirect
 	github.com/go-toolsmith/astcopy v1.1.0 // indirect
@@ -227,7 +232,7 @@ require (
 	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/goccy/go-yaml v1.18.0 // indirect
 	github.com/godoc-lint/godoc-lint v0.10.0 // indirect
-	github.com/gofrs/flock v0.12.1 // indirect
+	github.com/gofrs/flock v0.13.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e // indirect
 	github.com/gohugoio/hashstructure v0.5.0 // indirect
@@ -252,7 +257,7 @@ require (
 	github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
 	github.com/google/btree v1.1.3 // indirect
 	github.com/google/gnostic-models v0.7.0 // indirect
-	github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
+	github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
 	github.com/google/s2a-go v0.1.9 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.6.0 // indirect
@@ -274,6 +279,7 @@ require (
 	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 	github.com/hexops/gotextdiff v1.0.3 // indirect
 	github.com/huandu/xstrings v1.5.0 // indirect
+	github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jdkato/prose v1.2.1 // indirect
 	github.com/jgautheron/goconst v1.8.2 // indirect
@@ -310,7 +316,7 @@ require (
 	github.com/macabu/inamedparam v0.2.0 // indirect
 	github.com/magefile/mage v1.15.0 // indirect
 	github.com/magiconair/properties v1.8.10 // indirect
-	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/makeworld-the-better-one/dither/v2 v2.4.0 // indirect
 	github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
 	github.com/manuelarte/funcorder v0.5.0 // indirect
@@ -405,6 +411,7 @@ require (
 	github.com/tdewolff/minify/v2 v2.23.5 // indirect
 	github.com/tdewolff/parse/v2 v2.8.0 // indirect
 	github.com/tetafro/godot v1.5.4 // indirect
+	github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
 	github.com/tetratelabs/wazero v1.9.0 // indirect
 	github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
 	github.com/timonwong/loggercheck v0.11.0 // indirect
@@ -436,11 +443,12 @@ require (
 	go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
-	go.opentelemetry.io/otel v1.36.0 // indirect
-	go.opentelemetry.io/otel/metric v1.36.0 // indirect
+	go.opentelemetry.io/otel v1.37.0 // indirect
+	go.opentelemetry.io/otel/metric v1.37.0 // indirect
 	go.opentelemetry.io/otel/sdk v1.36.0 // indirect
 	go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
-	go.opentelemetry.io/otel/trace v1.36.0 // indirect
+	go.opentelemetry.io/otel/trace v1.37.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/automaxprocs v1.6.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.2 // indirect
@@ -469,6 +477,7 @@ require (
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
+	helm.sh/helm/v3 v3.19.0 // indirect
 	honnef.co/go/tools v0.6.1 // indirect
 	howett.net/plist v1.0.0 // indirect
 	k8s.io/api v0.34.1 // indirect
@@ -496,6 +505,7 @@ require (
 	mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
 	oras.land/oras-go/v2 v2.6.0 // indirect
 	rsc.io/qr v0.2.0 // indirect
+	sigs.k8s.io/controller-runtime v0.22.3 // indirect
 	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
 	sigs.k8s.io/kustomize/api v0.20.1 // indirect
 	sigs.k8s.io/kustomize/cmd/config v0.20.1 // indirect
diff --git a/hack/internal/tools/go.sum b/hack/internal/tools/go.sum
index 25c83e908..bc4a200ad 100644
--- a/hack/internal/tools/go.sum
+++ b/hack/internal/tools/go.sum
@@ -142,6 +142,8 @@ github.com/MirrexOne/unqueryvet v1.2.1 h1:M+zdXMq84g+E1YOLa7g7ExN3dWfZQrdDSTCM7g
 github.com/MirrexOne/unqueryvet v1.2.1/go.mod h1:IWwCwMQlSWjAIteW0t+28Q5vouyktfujzYznSIWiuOg=
 github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4=
 github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
+github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
+github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
 github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY=
 github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU=
 github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
@@ -317,6 +319,8 @@ github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nW
 github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
 github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
+github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -341,8 +345,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
 github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
-github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
-github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
+github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw=
+github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
 github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ=
 github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ=
 github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
@@ -381,6 +385,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ
 github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE=
+github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q=
 github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCGOhPElnMw=
 github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
 github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
@@ -403,12 +409,14 @@ github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
 github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
 github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
 github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
-github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
 github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U=
 github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
 github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
 github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
+github.com/extism/go-sdk v1.7.1 h1:lWJos6uY+tRFdlIHR+SJjwFDApY7OypS/2nMhiVQ9Sw=
+github.com/extism/go-sdk v1.7.1/go.mod h1:IT+Xdg5AZM9hVtpFUA+uZCJMge/hbvshl8bwzLtFyKA=
 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
 github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
 github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
@@ -417,6 +425,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
 github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
+github.com/fluxcd/cli-utils v0.36.0-flux.14 h1:I//AMVUXTc+M04UtIXArMXQZCazGMwfemodV1j/yG8c=
+github.com/fluxcd/cli-utils v0.36.0-flux.14/go.mod h1:uDo7BYOfbdmk/asnHuI0IQPl6u0FCgcN54AHDu3Y5As=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
@@ -446,8 +456,8 @@ github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01
 github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
 github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY=
 github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI=
-github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
-github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
+github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -465,14 +475,12 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
 github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
-github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
-github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
-github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
+github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
 github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
 github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
 github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
@@ -514,8 +522,8 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
 github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 github.com/godoc-lint/godoc-lint v0.10.0 h1:OcyrziBi18sQSEpib6NesVHEJ/Xcng97NunePBA48g4=
 github.com/godoc-lint/godoc-lint v0.10.0/go.mod h1:KleLcHu/CGSvkjUH2RvZyoK1MBC7pDQg4NxMYLcBBsw=
-github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
-github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
+github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
@@ -638,8 +646,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
-github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
+github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
+github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
 github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
@@ -709,6 +717,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
 github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA=
+github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
@@ -798,8 +808,8 @@ github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
 github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
 github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
 github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE=
 github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
 github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww=
@@ -1080,6 +1090,8 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR
 github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
 github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg=
 github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU=
+github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q=
+github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk=
 github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
 github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
 github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
@@ -1172,8 +1184,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
-go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
+go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
+go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs=
 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8=
@@ -1198,16 +1210,16 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsu
 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s=
 go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
 go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
-go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
-go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
+go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
+go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
 go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
 go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
 go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs=
 go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo=
 go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
 go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
-go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
-go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
+go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
+go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
@@ -1650,6 +1662,8 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
 gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
 helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k=
 helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc=
+helm.sh/helm/v4 v4.0.0 h1:Ppai7cygdmyxSR+JR9djUoVrRmyMI/yY5P5TBd25oHs=
+helm.sh/helm/v4 v4.0.0/go.mod h1:G1Y5AE+lJPQSAjh7nbXnhZrtGtxo+I6POSu9DruYiGI=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1719,6 +1733,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y=
+sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
 sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250407161039-6ad5c1dd4418 h1:04bBdoLjAbJzwbBTmcu/PukrGLTBj2suuFmQSEpmCH8=
 sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250407161039-6ad5c1dd4418/go.mod h1:bJr92VVEbIDLODpQ2NPN7Wlwsdv7uGzTAUvi9/gCgcc=
 sigs.k8s.io/controller-tools v0.19.0 h1:OU7jrPPiZusryu6YK0jYSjPqg8Vhf8cAzluP9XGI5uk=
diff --git a/hack/internal/tools/pinversion.go b/hack/internal/tools/pinversion.go
index e57218427..0664d5e17 100644
--- a/hack/internal/tools/pinversion.go
+++ b/hack/internal/tools/pinversion.go
@@ -28,7 +28,7 @@ import (
 	_ "github.com/onsi/ginkgo/v2/ginkgo"
 	_ "go.uber.org/mock/mockgen"
 	_ "gotest.tools/gotestsum"
-	_ "helm.sh/helm/v3/cmd/helm"
+	_ "helm.sh/helm/v4/cmd/helm"
 	_ "k8s.io/code-generator"
 	_ "sigs.k8s.io/controller-runtime/tools/setup-envtest"
 	_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
diff --git a/keps/2724-topology-aware-scheduling/README.md b/keps/2724-topology-aware-scheduling/README.md
index d8baa1d23..8dd3cdf7d 100644
--- a/keps/2724-topology-aware-scheduling/README.md
+++ b/keps/2724-topology-aware-scheduling/README.md
@@ -32,6 +32,9 @@
   - [Validation](#validation)
     - [PodSet Slice size validation](#podset-slice-size-validation)
   - [Internal APIs](#internal-apis)
+    - [Topology assignment representation](#topology-assignment-representation)
+      - [Until v1beta1](#until-v1beta1)
+      - [Since v1beta2](#since-v1beta2)
     - [Node failures](#node-failures)
       - [Until v0.13](#until-v013)
       - [Since v0.14](#since-v014)
@@ -72,6 +75,7 @@
   - [Drop dedicated TAS label](#drop-dedicated-tas-label)
   - [MostFreeCapacity algorithm](#mostfreecapacity-algorithm)
     - [Example](#example-3)
+  - [TopologyAssignmentSlices as separate CRD instances](#topologyassignmentslices-as-separate-crd-instances)
 <!-- /toc -->
 
 ## Summary
@@ -819,55 +823,49 @@ at each topology level to the specific subset of nodes.
 type PodSetAssignment struct {
   ...
 
-  // topologyAssignment indicates the topology assignment divided into
-  // topology domains corresponding to the lowest level of the topology.
-  // The assignment specifies the number of Pods to be scheduled per topology
-  // domain and specifies the node selectors for each topology domain, in the
-  // following way: the node selector keys are specified by the levels field
-  // (same for all domains), and the corresponding node selector value is
-  // specified by the domains.values subfield. If the TopologySpec.Levels field contains
-  // "kubernetes.io/hostname" label, topologyAssignment will contain data only for
-  // this label, and omit higher levels in the topology
-  //
-  // Example:
-  //
-  // topologyAssignment:
-  //   levels:
-  //   - cloud.provider.com/topology-block
-  //   - cloud.provider.com/topology-rack
-  //   domains:
-  //   - values: [block-1, rack-1]
-  //     count: 4
-  //   - values: [block-1, rack-2]
-  //     count: 2
-  //
-  // Here:
-  // - 4 Pods are to be scheduled on nodes matching the node selector:
-  //   cloud.provider.com/topology-block: block-1
-  //   cloud.provider.com/topology-rack: rack-1
-  // - 2 Pods are to be scheduled on nodes matching the node selector:
-  //   cloud.provider.com/topology-block: block-1
-  //   cloud.provider.com/topology-rack: rack-2
-  //
-  // Example:
-	// Below there is an equivalent of the above example assuming, Topology
-	// object defines kubernetes.io/hostname as the lowest level in topology.
-	// Hence we omit higher level of topologies, since the hostname label
-	// is sufficient to explicitly identify a proper node.
-  //
-  // topologyAssignment:
-  //   levels:
-  //   - kubernetes.io/hostname
-  //   domains:
-  //   - values: [hostname-1]
-  //     count: 4
-  //   - values: [hostname-2]
-  //     count: 2
-  //
   // +optional
   TopologyAssignment *TopologyAssignment `json:"topologyAssignment,omitempty"`
-}
+```
+
+The format of `TopologyAssignment` depends on the API version; see details [below](#topology-assignment-representation).
+
+Kueue uses the `kueue.x-k8s.io/topology` scheduling gate to delay the
+`nodeSelector` assignment, because different pods in the same PodSet may have
+different values:
+
+```golang
+const (
+  // TopologySchedulingGate is used to delay scheduling of a Pod until the
+  // nodeSelectors corresponding to the assigned topology domain are injected
+  // into the Pod.
+  TopologySchedulingGate = "kueue.x-k8s.io/topology"
+
+  // WorkloadAnnotation is an annotation set on the Job's PodTemplate to
+  // indicate the name of the admitted Workload corresponding to the Job. The
+  // annotation is set when starting the Job, and removed on stopping the Job.
+  WorkloadAnnotation = "kueue.x-k8s.io/workload"
+
+  // TASLabel is a label set on the Job's PodTemplate to indicate that the
+  // PodSet is admitted using TopologyAwareScheduling, and all Pods created
+  // from the Job's PodTemplate also have the label.
+  TASLabel = "kueue.x-k8s.io/tas"
+)
+```
+
+#### Topology assignment representation
+
+`TopologyAssignment` indicates the topology assignment divided into topology domains corresponding to the lowest level of the topology. The assignment specifies the number of Pods to be scheduled per topology domain and the node selectors for each topology domain, in the following way:
+
+- the node selector keys are specified by the `Levels` field (same for all domains),
+- the corresponding node selector values - and the Pod counts - are specified by the other field (`Domains` until v1beta1 or `Slices` since v1beta2).
 
+If the `Levels` field contains `kubernetes.io/hostname` label, the `TopologyAssignment` will contain data only for this label, and omit higher levels in the topology.
+
+##### Until v1beta1
+
+In the older format, each domain assignment is represented by a single entry in the `Domains` list. 
+
+```golang
 type TopologyAssignment struct {
   // levels is an ordered list of keys denoting the levels of the assigned
   // topology (i.e. node label keys), from the highest to the lowest level of
@@ -906,29 +904,204 @@ type TopologyDomainAssignment struct {
 }
 ```
 
-Kueue uses the `kueue.x-k8s.io/topology` scheduling gate to delay the
-`nodeSelector` assignment, because different pods in the same PodSet may have
-different values:
+Example:
+
+```yaml
+topologyAssignment:
+  levels:
+  - cloud.provider.com/topology-block
+  - cloud.provider.com/topology-rack
+  domains:
+  - values: [block-1, rack-1]
+    count: 4
+  - values: [block-1, rack-2]
+    count: 2
+```
+
+Here:
+- 4 Pods are to be scheduled on nodes matching the node selector:
+  ```
+  cloud.provider.com/topology-block: block-1
+  cloud.provider.com/topology-rack: rack-1
+  ```
+- 2 Pods are to be scheduled on nodes matching the node selector:
+  ```
+  cloud.provider.com/topology-block: block-1
+  cloud.provider.com/topology-rack: rack-2
+  ```
+
+Below there is an equivalent of the above example, assuming that the Topology
+object defines `kubernetes.io/hostname` as the lowest level in topology.
+Hence we omit higher topology levels, since the hostname label
+is sufficient to uniquely identify a particular node.
+
+```yaml
+topologyAssignment:
+  levels:
+  - kubernetes.io/hostname
+  domains:
+  - values: [hostname-1]
+    count: 4
+  - values: [hostname-2]
+    count: 2
+```
+
+##### Since v1beta2
+
+In v1beta2, the data format is reshaped, with the main goal of improving handling of huge workloads. The 3 main ideas here are:
+
+- splitting the whole assignment structure into slices,
+- broader use of "parallel lists" which should be "zipped together" to produce their meaning \
+  (just like a _single_ list `Levels` in the old format specified the topology levels used in _every_ domain of the assignment),
+- extracting common prefixes and suffixes of node names.
 
 ```golang
-const (
-  // TopologySchedulingGate is used to delay scheduling of a Pod until the
-  // nodeSelectors corresponding to the assigned topology domain are injected
-  // into the Pod.
-  TopologySchedulingGate = "kueue.x-k8s.io/topology"
+type TopologyAssignment struct {
+  // (same role & comments as in v1beta1)
+  Levels []string `json:"levels,omitempty"`
 
-  // WorkloadAnnotation is an annotation set on the Job's PodTemplate to
-  // indicate the name of the admitted Workload corresponding to the Job. The
-  // annotation is set when starting the Job, and removed on stopping the Job.
-  WorkloadAnnotation = "kueue.x-k8s.io/workload"
+  // slices represent topology assignments for subsets of pods of a workload.
+  // The full assignment is obtained as a union of all slices.
+  // +required
+  // +listType=atomic
+  // +kubebuilder:validation:MaxItems=1000
+  Slices []TopologyAssignmentSlice `json:"slices,omitempty"`
+}
 
-  // TASLabel is a label set on the Job's PodTemplate to indicate that the
-  // PodSet is admitted using TopologyAwareScheduling, and all Pods created
-  // from the Job's PodTemplate also have the label.
-  TASLabel = "kueue.x-k8s.io/tas"
-)
+// TopologyAssignmentSlice fully specifies the topology assignment for a subset of pods of a workload.
+type TopologyAssignmentSlice struct {
+  // domainCount is the number of domains covered by this slice.
+  // +required
+  // +kubebuilder:validation:Minimum=1
+  DomainCount int32 `json:"domainCount,omitempty"`
+
+  // valuesPerLevel has one entry for each of the Levels specified in the TopologyAssignment.
+  // The entry corresponding to a particular level specifies the placement of pods at that level.
+  // +required
+  // +listType=atomic
+  // +kubebuilder:validation:MinItems=1
+  // +kubebuilder:validation:MaxItems=16
+  ValuesPerLevel []TopologyAssignmentSliceLevelValues `json:"valuesPerLevel,omitempty"`
+
+  // podCounts specifies the number of pods allocated per each domain.
+  // +required
+  PodCounts TopologyAssignmentSlicePodCounts `json:"podCounts,omitempty"`
+}
+
+type TopologyAssignmentSliceLevelValues struct {
+  // universal, if set, specifies a single topology placement value (at a particular topology level)
+  // that applies to all pods in the current TopologyAssignmentSlice.
+  // Exactly one of universal, individual must be set.
+  // +optional
+  Universal *string `json:"universal,omitempty"`
+
+  // individual, if set, specifies multiple topology placement values (at a particular topology level)
+  // that apply to the pods in the current TopologyAssignmentSlice.
+  // Exactly one of universal, individual must be set.
+  // +optional
+  Individual *TopologyAssignmentSliceLevelIndividualValues `json:"individual,omitempty"`
+}
+
+type TopologyAssignmentSliceLevelIndividualValues struct {
+  // commonPrefix specifies a common prefix for all values in this slice assignment.
+  // It must be either nil pointer or a non-empty string.
+  // +optional
+  // +kubebuilder:validation:MaxLength=63
+  CommonPrefix *string `json:"commonPrefix,omitempty"`
+  // commonSuffix specifies a common suffix for all values in this slice assignment.
+  // It must be either nil pointer or a non-empty string.
+  // +optional
+  // +kubebuilder:validation:MaxLength=63
+  CommonSuffix *string `json:"commonSuffix,omitempty"`
+
+  // roots specifies the values in this assignment (excluding commonPrefix and commonSuffix, if non-empty).
+  // Its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+  // +required
+  // +listType=atomic
+  // +kubebuilder:validation:MinItems=1
+  // +kubebuilder:validation:MaxItems=100000
+  // +kubebuilder:validation:items:MaxLength=63
+  Roots []string `json:"roots,omitempty"`
+}
+
+type TopologyAssignmentSlicePodCounts struct {
+  // universal, if set, specifies the number of pods allocated in every domain in this slice.
+  // Exactly one of universal, individual must be set.
+  // +optional
+  // +kubebuilder:validation:Minimum=1
+  Universal *int32 `json:"universal,omitempty"`
+
+  // individual, if set, specifies the number of pods allocated in each domain in this slice.
+  // If set, its length must be equal to the "domainCount" field of the TopologyAssignmentSlice.
+  // Exactly one of universal, individual must be set.
+  // +optional
+  // +listType=atomic
+  // +kubebuilder:validation:MinItems=1
+  // +kubebuilder:validation:MaxItems=100000
+  // +kubebuilder:validation:items:Minimum=1
+  Individual []int32 `json:"podCounts,omitempty"`
+}
+```
+
+The above mentioned cross-validity rules (mutually exclusive fields, equal lengths, etc.) will be all expressed as `kubebuilder:validation:XValidation` rules on the respective "smallest common container types".
+
+Example (representing the same assignment as in the first example for the [old format](#until-v1beta1)):
+
+```yaml
+topologyAssignment:
+  levels:
+  - cloud.provider.com/topology-block
+  - cloud.provider.com/topology-rack
+  slices:
+  - domainCount: 2
+    valuesPerLevel:
+    - universal: block-1
+    - individual:
+        commonPrefix: rack-
+        roots: [1, 2]
+    podCounts:
+      individual: [4, 2]
 ```
 
+The above example has one slice, specifying 2 domains on 2 topology levels. On the block level, all domains take the same value (`block-1`), which allows using `universal`. On the rack level, the values diverge (`rack-1`) vs. (`rack-2`) but they still share a relatively long common prefix. The new representation allows deduplicating characters between these.
+
+Multiple slices may be used e.g. for a host-level assignment when the nodes being assigned are split into multiple "node pools". The following example illustrates this for an assignment of 12 pods on 12 nodes (1 per 1), where the nodes come from 2 pools (of size 5 and 7 respectively), with node names looking like `pool-X-node-Y`:
+
+```yaml
+topologyAssignment:
+  levels: [kubernetes.io/hostname]
+  slices:
+  - domainCount: 5
+    valuesPerLevel:
+    - individual:
+        commonPrefix: pool-1-node-
+        roots: [1, 2, 3, 4, 5]
+    podCounts:
+      universal: 1
+  - domainCount: 7
+    valuesPerLevel:
+    - individual:
+        commonPrefix: pool-2-node-
+        roots: [1, 2, 3, 4, 5, 6, 7]
+    podCounts:
+      universal: 1
+```
+
+The main motivation behind the new format is the etcd size limit of 1.5MiB per single resource, which currently restricts the number of nodes that can participate in the assignment for a single workload. (For the v1beta1 format, and the real-life node naming schemes of main K8s vendors, the limit is around 20-30k). The new format helps addressing this problem in 2 time perspectives:
+
+- In the short term, it increases the number of nodes which can fit into 1 etcd entry.
+
+  - By just using a single slice, with extracting common prefix and suffix of all node names, our simulations (for some real-life node naming schemes) suggested a limit of around 60k nodes.
+  
+  - Multiple slices allow optimizing even further, if desired. \
+    Our simulations of more complex algorithms (e.g. heuristic pruning of prefix tree) allowed fitting over 100k nodes. \
+    (However, at that point we reached a tradeoff between bytesize, encoding time, and conceptual simplicity. Resolving that tradeoff is out of scope of this design; the important thing is that the proposed data format supports various specific algorithms).
+
+- In the long term, as the number of nodes grows, at some point we'll inevitably hit the 1.5MiB limit anyway. \
+  When this happens, we foresee a need to store the slices as separate CRD instances (see [description](#topologyassignmentslices-as-separate-crd-instances) in the "Alternatives" section). \
+  While the v1beta2 format does not yet do that, by introducing `Slices` we come much closer to this. \
+  Once there is a need, we can promote (some of) `Slices` to instances of a standalone CRD - but the appropriate type system is already there.
+
 #### Node failures
 
 Initially we plan to support node becoming not ready (as indicated in Node
@@ -1566,3 +1739,17 @@ becomes apparent:
 **Reasons for discarding/deferring**
 Due to code simplicity concerns and a lack of use cases for the algorithm,
 the decision was made to remove it in favor of `BestFit`.
+
+### TopologyAssignmentSlices as separate CRD instances
+
+In the [v1beta2 format](#since-v1beta2) for TopologyAssignment, we introduce TopologyAssignmentSlices embedded in the WorkloadStatus. This helps fitting larger workloads within a single etcd entry, but still hits a scalability limit.
+
+One way of going beyond that limit would be to extract the slices into separate instances of a dedicated CRD (analogously to how [EndpointSlice](https://github.com/kubernetes/kubernetes/blob/3b632270e9b866ee8bf62e89377ae95987671b49/pkg/apis/discovery/types.go#L24-L29) has been introduced in K8s core). This would, in practice, allow storing arbitrarily many nodes, though at some cost. (See "Reasons for deferring" below).
+
+For these reasons, if we choose to do it, we would likely extract only "excess" slices, so that the TAS assignment for smaller workloads can be still kept inside WorkloadStatus.
+
+**Reasons for discarding/deferring**
+
+- Decreased UX of the API (some info delegated to other objects).
+- Decreased performance of Kueue scheduler (need to do more etcd reads and writes). \
+  (In particular, even when we end up using slices in separate CRDs, the "compression capabilities" introduced in v1beta2 are going to improve performance by reducing the necessary number of such slices).
diff --git a/keps/2724-topology-aware-scheduling/kep.yaml b/keps/2724-topology-aware-scheduling/kep.yaml
index b06fa80b4..b577a631b 100644
--- a/keps/2724-topology-aware-scheduling/kep.yaml
+++ b/keps/2724-topology-aware-scheduling/kep.yaml
@@ -3,6 +3,7 @@ kep-number: 2724
 authors:
   - "@mimowo"
   - "@mwielgus"
+  - "@olekzabl"
   - "@pbundyra"
 status: provisional
 creation-date: 2023-07-30
diff --git a/pkg/cache/queue/cluster_queue.go b/pkg/cache/queue/cluster_queue.go
index 4eb0ac459..84edb2792 100644
--- a/pkg/cache/queue/cluster_queue.go
+++ b/pkg/cache/queue/cluster_queue.go
@@ -21,6 +21,7 @@ import (
 	"slices"
 	"sync"
 
+	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -237,26 +238,29 @@ func (c *ClusterQueue) backoffWaitingTimeExpired(wInfo *workload.Info) bool {
 }
 
 // Delete removes the workload from ClusterQueue.
-func (c *ClusterQueue) Delete(w *kueue.Workload) {
+func (c *ClusterQueue) Delete(log logr.Logger, w *kueue.Workload) {
 	c.rwm.Lock()
 	defer c.rwm.Unlock()
-	c.delete(w)
+	c.delete(log, w)
 }
 
 // delete removes the workload from ClusterQueue without lock.
-func (c *ClusterQueue) delete(w *kueue.Workload) {
+func (c *ClusterQueue) delete(log logr.Logger, w *kueue.Workload) {
 	key := workload.Key(w)
 	delete(c.inadmissibleWorkloads, key)
 	c.heap.Delete(key)
 	c.forgetInflightByKey(key)
 	if c.sw.matches(key) {
+		if logV := log.V(5); logV.Enabled() {
+			logV.Info("Clearing sticky workload due to deletion", "clusterQueue", c.name, "workload", key)
+		}
 		c.sw.clear()
 	}
 }
 
 // DeleteFromLocalQueue removes all workloads belonging to this queue from
 // the ClusterQueue.
-func (c *ClusterQueue) DeleteFromLocalQueue(q *LocalQueue) {
+func (c *ClusterQueue) DeleteFromLocalQueue(log logr.Logger, q *LocalQueue) {
 	c.rwm.Lock()
 	defer c.rwm.Unlock()
 	for _, w := range q.items {
@@ -266,7 +270,7 @@ func (c *ClusterQueue) DeleteFromLocalQueue(q *LocalQueue) {
 		}
 	}
 	for _, w := range q.items {
-		c.delete(w.Obj)
+		c.delete(log, w.Obj)
 	}
 }
 
diff --git a/pkg/cache/queue/manager.go b/pkg/cache/queue/manager.go
index d3832a12e..3f8e83ef0 100644
--- a/pkg/cache/queue/manager.go
+++ b/pkg/cache/queue/manager.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"sync"
 
+	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/util/sets"
@@ -338,7 +339,7 @@ func (m *Manager) AddLocalQueue(ctx context.Context, q *kueue.LocalQueue) error
 	return nil
 }
 
-func (m *Manager) UpdateLocalQueue(q *kueue.LocalQueue) error {
+func (m *Manager) UpdateLocalQueue(log logr.Logger, q *kueue.LocalQueue) error {
 	m.Lock()
 	defer m.Unlock()
 	qImpl, ok := m.localQueues[queue.Key(q)]
@@ -348,7 +349,7 @@ func (m *Manager) UpdateLocalQueue(q *kueue.LocalQueue) error {
 	if qImpl.ClusterQueue != q.Spec.ClusterQueue {
 		oldCQ := m.hm.ClusterQueue(qImpl.ClusterQueue)
 		if oldCQ != nil {
-			oldCQ.DeleteFromLocalQueue(qImpl)
+			oldCQ.DeleteFromLocalQueue(log, qImpl)
 			oldCQ.deleteLocalQueue(queue.Key(q))
 		}
 		newCQ := m.hm.ClusterQueue(q.Spec.ClusterQueue)
@@ -362,7 +363,7 @@ func (m *Manager) UpdateLocalQueue(q *kueue.LocalQueue) error {
 	return nil
 }
 
-func (m *Manager) DeleteLocalQueue(q *kueue.LocalQueue) {
+func (m *Manager) DeleteLocalQueue(log logr.Logger, q *kueue.LocalQueue) {
 	m.Lock()
 	defer m.Unlock()
 	key := queue.Key(q)
@@ -372,7 +373,7 @@ func (m *Manager) DeleteLocalQueue(q *kueue.LocalQueue) {
 	}
 	cq := m.hm.ClusterQueue(qImpl.ClusterQueue)
 	if cq != nil {
-		cq.DeleteFromLocalQueue(qImpl)
+		cq.DeleteFromLocalQueue(log, qImpl)
 		cq.deleteLocalQueue(key)
 	}
 	if features.Enabled(features.LocalQueueMetrics) {
@@ -497,14 +498,14 @@ func (m *Manager) RequeueWorkload(ctx context.Context, info *workload.Info, reas
 	return added
 }
 
-func (m *Manager) DeleteWorkload(w *kueue.Workload) {
+func (m *Manager) DeleteWorkload(log logr.Logger, w *kueue.Workload) {
 	m.Lock()
 	defer m.Unlock()
-	m.deleteWorkloadFromQueueAndClusterQueue(w, queue.KeyFromWorkload(w))
+	m.deleteWorkloadFromQueueAndClusterQueue(log, w, queue.KeyFromWorkload(w))
 	m.DeleteSecondPassWithoutLock(w)
 }
 
-func (m *Manager) deleteWorkloadFromQueueAndClusterQueue(w *kueue.Workload, qKey queue.LocalQueueReference) {
+func (m *Manager) deleteWorkloadFromQueueAndClusterQueue(log logr.Logger, w *kueue.Workload, qKey queue.LocalQueueReference) {
 	q := m.localQueues[qKey]
 	if q == nil {
 		return
@@ -512,7 +513,7 @@ func (m *Manager) deleteWorkloadFromQueueAndClusterQueue(w *kueue.Workload, qKey
 	delete(q.items, workload.Key(w))
 	cq := m.hm.ClusterQueue(q.ClusterQueue)
 	if cq != nil {
-		cq.Delete(w)
+		cq.Delete(log, w)
 		m.reportPendingWorkloads(q.ClusterQueue, cq)
 	}
 	if features.Enabled(features.LocalQueueMetrics) {
@@ -627,11 +628,11 @@ func requeueWorkloadsCohortSubtree(ctx context.Context, m *Manager, cohort *coho
 
 // UpdateWorkload updates the workload to the corresponding queue or adds it if
 // it didn't exist. Returns whether the queue existed.
-func (m *Manager) UpdateWorkload(oldW, w *kueue.Workload, opts ...workload.InfoOption) error {
+func (m *Manager) UpdateWorkload(log logr.Logger, oldW, w *kueue.Workload, opts ...workload.InfoOption) error {
 	m.Lock()
 	defer m.Unlock()
 	if oldW.Spec.QueueName != w.Spec.QueueName {
-		m.deleteWorkloadFromQueueAndClusterQueue(w, queue.KeyFromWorkload(oldW))
+		m.deleteWorkloadFromQueueAndClusterQueue(log, w, queue.KeyFromWorkload(oldW))
 	}
 	return m.AddOrUpdateWorkloadWithoutLock(w, opts...)
 }
diff --git a/pkg/cache/scheduler/tas_balanced_placement.go b/pkg/cache/scheduler/tas_balanced_placement.go
index 758aaed60..b777da3cf 100644
--- a/pkg/cache/scheduler/tas_balanced_placement.go
+++ b/pkg/cache/scheduler/tas_balanced_placement.go
@@ -237,7 +237,7 @@ func sortDomainsByCapacityAndEntropy(domains []*domain) {
 
 // findBestDomainsForBalancedPlacement evaluates domains for balanced placement.
 // It returns the best set of domains, the balance threshold, and whether a balanced placement is possible.
-func findBestDomainsForBalancedPlacement(s *TASFlavorSnapshot, levelIdx, sliceLevelIdx int, count, leaderCount, sliceSize int32) ([]*domain, int32, bool) {
+func findBestDomainsForBalancedPlacement(s *TASFlavorSnapshot, levelIdx, sliceLevelIdx int, count, leaderCount, sliceSize int32) ([]*domain, int32) {
 	// check if balanced placement is possible: look one level above the preferred level
 	// see if any (single) domain on that level fits the request and compute for each of
 	// them the balance threshold value
@@ -254,7 +254,6 @@ func findBestDomainsForBalancedPlacement(s *TASFlavorSnapshot, levelIdx, sliceLe
 	var bestThreshold int32
 	var bestDomainCountOnRequestedLevel int32
 	var currFitDomain []*domain
-	useBalancedPlacement := false
 
 	for _, requestedLevelSiblingDomains := range requestedLevelDomainsToConsider {
 		lowerLevelDomains := getLowerLevelDomains(s, requestedLevelSiblingDomains, levelIdx, sliceLevelIdx)
@@ -270,11 +269,10 @@ func findBestDomainsForBalancedPlacement(s *TASFlavorSnapshot, levelIdx, sliceLe
 				bestThreshold = threshold
 				bestDomainCountOnRequestedLevel = requestedLevelDomainCount
 				currFitDomain = requestedLevelSiblingDomains
-				useBalancedPlacement = true
 			}
 		}
 	}
-	return currFitDomain, bestThreshold, useBalancedPlacement
+	return currFitDomain, bestThreshold
 }
 
 // applyBalancedPlacementAlgorithm applies the balanced placement algorithm to determine domain assignments.
@@ -318,3 +316,30 @@ func getLowerLevelDomains(s *TASFlavorSnapshot, domains []*domain, levelIdx, sli
 	}
 	return domains
 }
+
+func clearState(d *domain) {
+	d.state = int32(0)
+	d.sliceState = int32(0)
+	d.stateWithLeader = int32(0)
+	d.sliceStateWithLeader = int32(0)
+	d.leaderState = int32(0)
+	for _, child := range d.children {
+		clearState(child)
+	}
+}
+
+func (s *TASFlavorSnapshot) pruneDomainsBelowThreshold(domains []*domain, threshold int32, sliceSize int32, sliceLevelIdx int, level int) {
+	for _, d := range domains {
+		for _, c := range d.children {
+			if c.sliceStateWithLeader < threshold {
+				clearState(c)
+			}
+		}
+	}
+	for _, d := range domains {
+		d.state, d.sliceState, d.stateWithLeader, d.sliceStateWithLeader, d.leaderState = s.fillInCountsHelper(d, sliceSize, sliceLevelIdx, level)
+		if d.sliceStateWithLeader < threshold {
+			clearState(d)
+		}
+	}
+}
diff --git a/pkg/cache/scheduler/tas_flavor_snapshot.go b/pkg/cache/scheduler/tas_flavor_snapshot.go
index 6f5fce89d..8a9d7ea89 100644
--- a/pkg/cache/scheduler/tas_flavor_snapshot.go
+++ b/pkg/cache/scheduler/tas_flavor_snapshot.go
@@ -380,7 +380,7 @@ func (r TASAssignmentsResult) Failure() *FailureInfo {
 }
 
 type tasPodSetAssignmentResult struct {
-	TopologyAssignment *kueue.TopologyAssignment
+	TopologyAssignment *utiltas.TopologyAssignment
 	FailureReason      string
 }
 
@@ -462,7 +462,7 @@ func (s *TASFlavorSnapshot) FindTopologyAssignmentsForFlavor(flavorTASRequests F
 				}
 				// We deepCopy the existing TopologyAssignment, so if we delete unwanted domain,
 				// And there is no fit, we have the original newAssignment to retry with
-				newAssignment, replacementAssignment, reason := s.findReplacementAssignment(&tr, psa.TopologyAssignment.DeepCopy(), opts.workload, assumedUsage)
+				newAssignment, replacementAssignment, reason := s.findReplacementAssignment(&tr, utiltas.InternalFrom(psa.TopologyAssignment), opts.workload, assumedUsage)
 				result[tr.PodSet.Name] = tasPodSetAssignmentResult{TopologyAssignment: newAssignment, FailureReason: reason}
 				if reason != "" {
 					return result
@@ -508,7 +508,7 @@ func findLeaderAndWorkers(trs FlavorTASRequests) (*TASPodSetRequests, TASPodSetR
 // findReplacementAssignment finds the topology assignment for the replacement node
 // it return new corrected topologyAssignment, a replacement topologyAssignment used to patched the old, faulty one, and
 // reason if finding fails
-func (s *TASFlavorSnapshot) findReplacementAssignment(tr *TASPodSetRequests, existingAssignment *kueue.TopologyAssignment, wl *kueue.Workload, assumedUsage map[utiltas.TopologyDomainID]resources.Requests) (*kueue.TopologyAssignment, *kueue.TopologyAssignment, string) {
+func (s *TASFlavorSnapshot) findReplacementAssignment(tr *TASPodSetRequests, existingAssignment *utiltas.TopologyAssignment, wl *kueue.Workload, assumedUsage map[utiltas.TopologyDomainID]resources.Requests) (*utiltas.TopologyAssignment, *utiltas.TopologyAssignment, string) {
 	tr.Count = deleteDomain(existingAssignment, wl.Status.UnhealthyNodes[0].Name)
 	if isStale, staleDomain := s.IsTopologyAssignmentStale(existingAssignment); isStale {
 		return nil, nil, fmt.Sprintf("Cannot replace the node, because the existing topologyAssignment is invalid, as it contains the stale domain %v", staleDomain)
@@ -530,7 +530,7 @@ func (s *TASFlavorSnapshot) findReplacementAssignment(tr *TASPodSetRequests, exi
 	return newAssignment, replacementAssignment[tr.PodSet.Name], ""
 }
 
-func addAssumedUsage(assumedUsage map[utiltas.TopologyDomainID]resources.Requests, ta *kueue.TopologyAssignment, tr *TASPodSetRequests) {
+func addAssumedUsage(assumedUsage map[utiltas.TopologyDomainID]resources.Requests, ta *utiltas.TopologyAssignment, tr *TASPodSetRequests) {
 	for _, domain := range ta.Domains {
 		domainID := utiltas.DomainID(domain.Values)
 		if assumedUsage[domainID] == nil {
@@ -552,7 +552,7 @@ func findPSA(wl *kueue.Workload, psName kueue.PodSetReference) *kueue.PodSetAssi
 	return nil
 }
 
-func (s *TASFlavorSnapshot) requiredReplacementDomain(tr *TASPodSetRequests, ta *kueue.TopologyAssignment) utiltas.TopologyDomainID {
+func (s *TASFlavorSnapshot) requiredReplacementDomain(tr *TASPodSetRequests, ta *utiltas.TopologyAssignment) utiltas.TopologyDomainID {
 	key := s.levelKeyWithImpliedFallback(tr)
 	if key == nil {
 		return ""
@@ -590,7 +590,7 @@ func (s *TASFlavorSnapshot) requiredReplacementDomain(tr *TASPodSetRequests, ta
 // IsTopologyAssignmentStale indicates whether the topologyAssignment have Nodes
 // that don't exists in the snapshot. It may be cause e.g. by Node deletion, or change
 // in Node's NodeReady condition
-func (s *TASFlavorSnapshot) IsTopologyAssignmentStale(ta *kueue.TopologyAssignment) (bool, string) {
+func (s *TASFlavorSnapshot) IsTopologyAssignmentStale(ta *utiltas.TopologyAssignment) (bool, string) {
 	for _, domain := range ta.Domains {
 		if _, found := s.domains[utiltas.DomainID(domain.Values)]; !found {
 			return true, domain.Values[0]
@@ -600,9 +600,9 @@ func (s *TASFlavorSnapshot) IsTopologyAssignmentStale(ta *kueue.TopologyAssignme
 }
 
 // deleteDomain deletes the domain the has faulty node and returns number of affected pods by the node
-func deleteDomain(currentTopologyAssignment *kueue.TopologyAssignment, unhealthyNode string) int32 {
+func deleteDomain(currentTopologyAssignment *utiltas.TopologyAssignment, unhealthyNode string) int32 {
 	var noAffectedPods int32 = 0
-	updatedAssignment := make([]kueue.TopologyDomainAssignment, 0, len(currentTopologyAssignment.Domains))
+	updatedAssignment := make([]utiltas.TopologyDomainAssignment, 0, len(currentTopologyAssignment.Domains))
 	for _, domain := range currentTopologyAssignment.Domains {
 		if domain.Values[len(domain.Values)-1] == unhealthyNode {
 			noAffectedPods = domain.Count
@@ -614,7 +614,7 @@ func deleteDomain(currentTopologyAssignment *kueue.TopologyAssignment, unhealthy
 	return noAffectedPods
 }
 
-func (s *TASFlavorSnapshot) findIncompleteSliceDomain(tr *TASPodSetRequests, ta *kueue.TopologyAssignment, missingCount int32) utiltas.TopologyDomainID {
+func (s *TASFlavorSnapshot) findIncompleteSliceDomain(tr *TASPodSetRequests, ta *utiltas.TopologyAssignment, missingCount int32) utiltas.TopologyDomainID {
 	// this function assumes that all assignments are at the hostname level
 	sliceTopologyKey := s.sliceLevelKeyWithDefault(tr.PodSet.TopologyRequest, s.lowestLevel())
 	sliceLevelIdx, found := s.resolveLevelIdx(sliceTopologyKey)
@@ -665,7 +665,7 @@ func (s *TASFlavorSnapshot) findTopologyAssignment(
 	workersTasPodSetRequests TASPodSetRequests,
 	leaderTasPodSetRequests *TASPodSetRequests,
 	assumedUsage map[utiltas.TopologyDomainID]resources.Requests,
-	simulateEmpty bool, requiredReplacementDomain utiltas.TopologyDomainID) (map[kueue.PodSetReference]*kueue.TopologyAssignment, string) {
+	simulateEmpty bool, requiredReplacementDomain utiltas.TopologyDomainID) (map[kueue.PodSetReference]*utiltas.TopologyAssignment, string) {
 	requests := workersTasPodSetRequests.SinglePodRequests.Clone()
 	requests.Add(resources.Requests{corev1.ResourcePods: 1})
 
@@ -746,8 +746,8 @@ func (s *TASFlavorSnapshot) findTopologyAssignment(
 	var useBalancedPlacement bool
 	if features.Enabled(features.TASBalancedPlacement) && !required && !unconstrained {
 		var bestThreshold int32
-		currFitDomain, bestThreshold, useBalancedPlacement = findBestDomainsForBalancedPlacement(s, levelIdx, sliceLevelIdx, count, leaderCount, sliceSize)
-
+		currFitDomain, bestThreshold = findBestDomainsForBalancedPlacement(s, levelIdx, sliceLevelIdx, count, leaderCount, sliceSize)
+		useBalancedPlacement = bestThreshold > 0
 		if useBalancedPlacement {
 			currFitDomain, reason = applyBalancedPlacementAlgorithm(s, levelIdx, sliceLevelIdx, count, leaderCount, sliceSize, bestThreshold, unconstrained, currFitDomain)
 			if len(reason) > 0 {
@@ -788,7 +788,7 @@ func (s *TASFlavorSnapshot) findTopologyAssignment(
 		}
 	}
 
-	assignments := make(map[kueue.PodSetReference]*kueue.TopologyAssignment)
+	assignments := make(map[kueue.PodSetReference]*utiltas.TopologyAssignment)
 
 	if leaderTasPodSetRequests != nil {
 		var leaderFitDomains []*domain
@@ -829,17 +829,17 @@ func getSliceSizeWithSinglePodAsDefault(podSetTopologyRequest *kueue.PodSetTopol
 }
 
 // Merges two topology assignments keeping the lexicographical order of levelValues
-func (s *TASFlavorSnapshot) mergeTopologyAssignments(a, b *kueue.TopologyAssignment) *kueue.TopologyAssignment {
+func (s *TASFlavorSnapshot) mergeTopologyAssignments(a, b *utiltas.TopologyAssignment) *utiltas.TopologyAssignment {
 	nodeLevel := len(s.levelKeys) - 1
-	sortedDomains := make([]kueue.TopologyDomainAssignment, 0, len(a.Domains)+len(b.Domains))
+	sortedDomains := make([]utiltas.TopologyDomainAssignment, 0, len(a.Domains)+len(b.Domains))
 	sortedDomains = append(sortedDomains, a.Domains...)
 	sortedDomains = append(sortedDomains, b.Domains...)
-	slices.SortFunc(sortedDomains, func(a, b kueue.TopologyDomainAssignment) int {
+	slices.SortFunc(sortedDomains, func(a, b utiltas.TopologyDomainAssignment) int {
 		aDomain := s.domainsPerLevel[nodeLevel][utiltas.DomainID(a.Values)]
 		bDomain := s.domainsPerLevel[nodeLevel][utiltas.DomainID(b.Values)]
 		return cmp.Compare(utiltas.DomainID(aDomain.levelValues), utiltas.DomainID(bDomain.levelValues))
 	})
-	mergedDomains := make([]kueue.TopologyDomainAssignment, 0, len(sortedDomains))
+	mergedDomains := make([]utiltas.TopologyDomainAssignment, 0, len(sortedDomains))
 	for _, domain := range sortedDomains {
 		if canMerge(mergedDomains, domain) {
 			mergedDomains[len(mergedDomains)-1].Count += domain.Count
@@ -847,13 +847,13 @@ func (s *TASFlavorSnapshot) mergeTopologyAssignments(a, b *kueue.TopologyAssignm
 			mergedDomains = append(mergedDomains, domain)
 		}
 	}
-	return &kueue.TopologyAssignment{
+	return &utiltas.TopologyAssignment{
 		Levels:  a.Levels,
 		Domains: mergedDomains,
 	}
 }
 
-func canMerge(mergedDomains []kueue.TopologyDomainAssignment, domain kueue.TopologyDomainAssignment) bool {
+func canMerge(mergedDomains []utiltas.TopologyDomainAssignment, domain utiltas.TopologyDomainAssignment) bool {
 	if len(mergedDomains) == 0 {
 		return false
 	}
@@ -1198,9 +1198,9 @@ func (s *TASFlavorSnapshot) updateCountsToMinimumGeneric(domains []*domain, coun
 }
 
 // buildTopologyAssignmentForLevels build TopologyAssignment for levels starting from levelIdx
-func (s *TASFlavorSnapshot) buildTopologyAssignmentForLevels(domains []*domain, levelIdx int) *kueue.TopologyAssignment {
-	assignment := &kueue.TopologyAssignment{
-		Domains: make([]kueue.TopologyDomainAssignment, 0),
+func (s *TASFlavorSnapshot) buildTopologyAssignmentForLevels(domains []*domain, levelIdx int) *utiltas.TopologyAssignment {
+	assignment := &utiltas.TopologyAssignment{
+		Domains: make([]utiltas.TopologyDomainAssignment, 0),
 	}
 	assignment.Levels = s.levelKeys[levelIdx:]
 	for _, domain := range domains {
@@ -1208,7 +1208,7 @@ func (s *TASFlavorSnapshot) buildTopologyAssignmentForLevels(domains []*domain,
 			// It may happen when PodSet count is 0.
 			continue
 		}
-		assignment.Domains = append(assignment.Domains, kueue.TopologyDomainAssignment{
+		assignment.Domains = append(assignment.Domains, utiltas.TopologyDomainAssignment{
 			Values: domain.levelValues[levelIdx:],
 			Count:  domain.state,
 		})
@@ -1216,7 +1216,7 @@ func (s *TASFlavorSnapshot) buildTopologyAssignmentForLevels(domains []*domain,
 	return assignment
 }
 
-func (s *TASFlavorSnapshot) buildAssignment(domains []*domain) *kueue.TopologyAssignment {
+func (s *TASFlavorSnapshot) buildAssignment(domains []*domain) *utiltas.TopologyAssignment {
 	// lex sort domains by their levelValues instead of IDs, as leaves' IDs can only contain the hostname
 	slices.SortFunc(domains, func(a, b *domain) int {
 		return slices.Compare(a.levelValues, b.levelValues)
@@ -1432,33 +1432,6 @@ func (s *TASFlavorSnapshot) notFitMessage(slicesFitCount, totalRequestsSlicesCou
 	return fmt.Sprintf("topology %q allows to fit only %d out of %d slice(s)", s.topologyName, slicesFitCount, totalRequestsSlicesCount)
 }
 
-func clearState(d *domain) {
-	d.state = int32(0)
-	d.sliceState = int32(0)
-	d.stateWithLeader = int32(0)
-	d.sliceStateWithLeader = int32(0)
-	d.leaderState = int32(0)
-	for _, child := range d.children {
-		clearState(child)
-	}
-}
-
-func (s *TASFlavorSnapshot) pruneDomainsBelowThreshold(domains []*domain, threshold int32, sliceSize int32, sliceLevelIdx int, level int) {
-	for _, d := range domains {
-		for _, c := range d.children {
-			if c.sliceStateWithLeader < threshold {
-				clearState(c)
-			}
-		}
-	}
-	for _, d := range domains {
-		d.state, d.sliceState, d.stateWithLeader, d.sliceStateWithLeader, d.leaderState = s.fillInCountsHelper(d, sliceSize, sliceLevelIdx, level)
-		if d.sliceStateWithLeader < threshold {
-			clearState(d)
-		}
-	}
-}
-
 func slicesRequested(tr *kueue.PodSetTopologyRequest) bool {
 	return tr != nil && tr.PodSetSliceRequiredTopology != nil && tr.PodSetSliceSize != nil
 }
diff --git a/pkg/controller/core/core.go b/pkg/controller/core/core.go
index b30c4e582..b0a45aa39 100644
--- a/pkg/controller/core/core.go
+++ b/pkg/controller/core/core.go
@@ -26,6 +26,7 @@ import (
 	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
 	"sigs.k8s.io/kueue/pkg/constants"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/fairsharing"
 )
 
 const (
@@ -49,14 +50,11 @@ func SetupControllers(mgr ctrl.Manager, qManager *qcache.Manager, cc *schdcache.
 		return "LocalQueue", err
 	}
 
-	var fairSharingEnabled bool
-	if cfg.FairSharing != nil {
-		fairSharingEnabled = cfg.FairSharing.Enable
-	}
-
+	fairSharingEnabled := fairsharing.Enabled(cfg.FairSharing)
 	watchers := []ClusterQueueUpdateWatcher{rfRec, acRec}
 	if features.Enabled(features.HierarchicalCohorts) {
-		cohortRec := NewCohortReconciler(mgr.GetClient(), cc, qManager, CohortReconcilerWithFairSharing(fairSharingEnabled))
+		cohortRec := NewCohortReconciler(mgr.GetClient(), cc, qManager,
+			CohortReconcilerWithFairSharing(fairSharingEnabled))
 		if err := cohortRec.SetupWithManager(mgr, cfg); err != nil {
 			return "Cohort", err
 		}
diff --git a/pkg/controller/core/localqueue_controller.go b/pkg/controller/core/localqueue_controller.go
index c65f9381a..b0c56ac16 100644
--- a/pkg/controller/core/localqueue_controller.go
+++ b/pkg/controller/core/localqueue_controller.go
@@ -217,8 +217,9 @@ func (r *LocalQueueReconciler) Delete(e event.TypedDeleteEvent[*kueue.LocalQueue
 		metrics.ClearLocalQueueResourceMetrics(localQueueReferenceFromLocalQueue(e.Object))
 	}
 
-	r.log.V(2).Info("LocalQueue delete event", "localQueue", klog.KObj(e.Object))
-	r.queues.DeleteLocalQueue(e.Object)
+	log := r.log.WithValues("localQueue", klog.KObj(e.Object))
+	log.V(2).Info("LocalQueue delete event")
+	r.queues.DeleteLocalQueue(log, e.Object)
 	r.cache.DeleteLocalQueue(e.Object)
 	return true
 }
@@ -236,7 +237,7 @@ func (r *LocalQueueReconciler) Update(e event.TypedUpdateEvent[*kueue.LocalQueue
 
 	if newStopPolicy == oldStopPolicy {
 		if newStopPolicy == kueue.None {
-			if err := r.queues.UpdateLocalQueue(e.ObjectNew); err != nil {
+			if err := r.queues.UpdateLocalQueue(log, e.ObjectNew); err != nil {
 				log.Error(err, "Failed to update queue in the queueing system")
 			}
 		}
@@ -254,7 +255,7 @@ func (r *LocalQueueReconciler) Update(e event.TypedUpdateEvent[*kueue.LocalQueue
 		return true
 	}
 
-	r.queues.DeleteLocalQueue(e.ObjectOld)
+	r.queues.DeleteLocalQueue(log, e.ObjectOld)
 
 	return true
 }
diff --git a/pkg/controller/core/workload_controller.go b/pkg/controller/core/workload_controller.go
index 1ef0d461f..378e9cc72 100644
--- a/pkg/controller/core/workload_controller.go
+++ b/pkg/controller/core/workload_controller.go
@@ -785,7 +785,7 @@ func (r *WorkloadReconciler) Delete(e event.TypedDeleteEvent[*kueue.Workload]) b
 
 	// Even if the state is unknown, the last cached state tells us whether the
 	// workload was in the queues and should be cleared from them.
-	r.queues.DeleteWorkload(e.Object)
+	r.queues.DeleteWorkload(log, e.Object)
 
 	return true
 }
@@ -824,7 +824,7 @@ func (r *WorkloadReconciler) Update(e event.TypedUpdateEvent[*kueue.Workload]) b
 			log.V(2).Info("Workload will not be queued because the workload is not active")
 		}
 		// The workload could have been in the queues if we missed an event.
-		r.queues.DeleteWorkload(e.ObjectNew)
+		r.queues.DeleteWorkload(log, e.ObjectNew)
 
 		// trigger the move of associated inadmissibleWorkloads, if there are any.
 		r.queues.QueueAssociatedInadmissibleWorkloadsAfter(ctx, e.ObjectNew, func() {
@@ -841,13 +841,13 @@ func (r *WorkloadReconciler) Update(e event.TypedUpdateEvent[*kueue.Workload]) b
 		if features.Enabled(features.DynamicResourceAllocation) && workload.HasDRA(e.ObjectNew) {
 			log.V(2).Info("Skipping queue update for DRA workload - handled in Reconcile")
 		} else {
-			err := r.queues.UpdateWorkload(e.ObjectOld, wlCopy)
+			err := r.queues.UpdateWorkload(log, e.ObjectOld, wlCopy)
 			if err != nil {
 				log.V(2).Info("ignored an error for now", "error", err)
 			}
 		}
 	case prevStatus == workload.StatusPending && (status == workload.StatusQuotaReserved || status == workload.StatusAdmitted):
-		r.queues.DeleteWorkload(e.ObjectOld)
+		r.queues.DeleteWorkload(log, e.ObjectOld)
 		if !r.cache.AddOrUpdateWorkload(log, wlCopy) {
 			log.V(2).Info("ClusterQueue for workload didn't exist; ignored for now")
 		}
diff --git a/pkg/controller/tas/node_failure_controller.go b/pkg/controller/tas/node_failure_controller.go
index 065b28520..7eba1d9e5 100644
--- a/pkg/controller/tas/node_failure_controller.go
+++ b/pkg/controller/tas/node_failure_controller.go
@@ -191,8 +191,8 @@ func (r *nodeFailureReconciler) getWorkloadsOnNode(ctx context.Context, nodeName
 			if !utiltas.IsLowestLevelHostname(topologyAssignment.Levels) {
 				continue
 			}
-			for _, domain := range topologyAssignment.Domains {
-				if nodeName == domain.Values[len(domain.Values)-1] {
+			for value := range utiltas.LowestLevelValues(topologyAssignment) {
+				if value == nodeName {
 					tasWorkloadsOnNode.Insert(types.NamespacedName{Name: wl.Name, Namespace: wl.Namespace})
 				}
 			}
diff --git a/pkg/controller/tas/topology_ungater.go b/pkg/controller/tas/topology_ungater.go
index 8a08092c7..16132c797 100644
--- a/pkg/controller/tas/topology_ungater.go
+++ b/pkg/controller/tas/topology_ungater.go
@@ -302,7 +302,7 @@ func podsToUngateInfo(
 	psa *kueue.PodSetAssignment,
 	podToUngateWithDomain []podWithDomain) []podWithUngateInfo {
 	domainIDToLabelValues := make(map[utiltas.TopologyDomainID][]string)
-	for _, psaDomain := range psa.TopologyAssignment.Domains {
+	for psaDomain := range utiltas.InternalSeqFrom(psa.TopologyAssignment) {
 		domainID := utiltas.DomainID(psaDomain.Values)
 		domainIDToLabelValues[domainID] = psaDomain.Values
 	}
@@ -335,13 +335,13 @@ func assignGatedPodsToDomainsByRanks(
 	psa *kueue.PodSetAssignment,
 	rankToGatedPod map[int]*corev1.Pod) []podWithDomain {
 	toUngate := make([]podWithDomain, 0)
-	totalCount := 0
-	for i := range psa.TopologyAssignment.Domains {
-		totalCount += int(psa.TopologyAssignment.Domains[i].Count)
+	totalPodCount := 0
+	for count := range utiltas.PodCounts(psa.TopologyAssignment) {
+		totalPodCount += int(count)
 	}
-	rankToDomainID := make([]utiltas.TopologyDomainID, totalCount)
+	rankToDomainID := make([]utiltas.TopologyDomainID, totalPodCount)
 	index := int32(0)
-	for _, domain := range psa.TopologyAssignment.Domains {
+	for domain := range utiltas.InternalSeqFrom(psa.TopologyAssignment) {
 		for s := range domain.Count {
 			rankToDomainID[index+s] = utiltas.DomainID(domain.Values)
 		}
@@ -378,7 +378,7 @@ func assignGatedPodsToDomainsGreedy(
 		"domainIDToUngatedCount", domainIDToUngatedCnt,
 		"levelKeys", levelKeys)
 	toUngate := make([]podWithDomain, 0)
-	for _, psaDomain := range psa.TopologyAssignment.Domains {
+	for psaDomain := range utiltas.InternalSeqFrom(psa.TopologyAssignment) {
 		domainID := utiltas.DomainID(psaDomain.Values)
 		ungatedInDomainCnt := domainIDToUngatedCnt[domainID]
 		remainingUngatedInDomain := max(psaDomain.Count-ungatedInDomainCnt, 0)
diff --git a/pkg/scheduler/flavorassigner/flavorassigner.go b/pkg/scheduler/flavorassigner/flavorassigner.go
index 2306acc85..0bfc2ac07 100644
--- a/pkg/scheduler/flavorassigner/flavorassigner.go
+++ b/pkg/scheduler/flavorassigner/flavorassigner.go
@@ -41,6 +41,7 @@ import (
 	preemptioncommon "sigs.k8s.io/kueue/pkg/scheduler/preemption/common"
 	utilmaps "sigs.k8s.io/kueue/pkg/util/maps"
 	"sigs.k8s.io/kueue/pkg/util/orderedgroups"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	"sigs.k8s.io/kueue/pkg/workload"
 )
 
@@ -239,7 +240,7 @@ type PodSetAssignment struct {
 	Requests corev1.ResourceList
 	Count    int32
 
-	TopologyAssignment     *kueue.TopologyAssignment
+	TopologyAssignment     *tas.TopologyAssignment
 	DelayedTopologyRequest *kueue.DelayedTopologyRequestState
 }
 
@@ -287,7 +288,7 @@ func (psa *PodSetAssignment) toAPI() kueue.PodSetAssignment {
 		Flavors:                flavors,
 		ResourceUsage:          psa.Requests,
 		Count:                  ptr.To(psa.Count),
-		TopologyAssignment:     psa.TopologyAssignment.DeepCopy(),
+		TopologyAssignment:     tas.V1Beta2From(psa.TopologyAssignment),
 		DelayedTopologyRequest: psa.DelayedTopologyRequest,
 	}
 }
@@ -551,7 +552,7 @@ func (a *FlavorAssigner) assignFlavors(log logr.Logger, counts []int32) Assignme
 				psAssignment.DelayedTopologyRequest = ptr.To(*podSet.DelayedTopologyRequest)
 			}
 			if podSet.TopologyRequest != nil {
-				psAssignment.TopologyAssignment = a.wl.Obj.Status.Admission.PodSetAssignments[i].TopologyAssignment
+				psAssignment.TopologyAssignment = tas.InternalFrom(a.wl.Obj.Status.Admission.PodSetAssignments[i].TopologyAssignment)
 			}
 		}
 
diff --git a/pkg/scheduler/flavorassigner/tas_flavorassigner.go b/pkg/scheduler/flavorassigner/tas_flavorassigner.go
index 957207476..30881a174 100644
--- a/pkg/scheduler/flavorassigner/tas_flavorassigner.go
+++ b/pkg/scheduler/flavorassigner/tas_flavorassigner.go
@@ -25,6 +25,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	"sigs.k8s.io/kueue/pkg/workload"
 )
 
@@ -57,7 +58,7 @@ func (a *Assignment) WorkloadsTopologyRequests(wl *workload.Info, cq *schdcache.
 }
 
 func (psa *PodSetAssignment) HasUnhealthyNode(wl *workload.Info) bool {
-	return workload.HasUnhealthyNodes(wl.Obj) && slices.ContainsFunc(psa.TopologyAssignment.Domains, func(domain kueue.TopologyDomainAssignment) bool {
+	return workload.HasUnhealthyNodes(wl.Obj) && slices.ContainsFunc(psa.TopologyAssignment.Domains, func(domain tas.TopologyDomainAssignment) bool {
 		return workload.HasUnhealthyNode(wl.Obj, domain.Values[len(domain.Values)-1])
 	})
 }
diff --git a/pkg/scheduler/preemption/fairsharing/strategy.go b/pkg/scheduler/preemption/fairsharing/strategy.go
index 34c3b2efe..8714e06f5 100644
--- a/pkg/scheduler/preemption/fairsharing/strategy.go
+++ b/pkg/scheduler/preemption/fairsharing/strategy.go
@@ -16,7 +16,10 @@ limitations under the License.
 
 package fairsharing
 
-import schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
+import (
+	config "sigs.k8s.io/kueue/apis/config/v1beta2"
+	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
+)
 
 // PreemptorNewShare is the DominantResourceShare of the Preemptor
 // after the incoming workload's usage has been added. It is used for
@@ -43,3 +46,7 @@ func LessThanOrEqualToFinalShare(preemptorNewShare PreemptorNewShare, _ TargetOl
 func LessThanInitialShare(preemptorNewShare PreemptorNewShare, targetOldShare TargetOldShare, _ TargetNewShare) bool {
 	return schdcache.CompareDRS(schdcache.DRS(preemptorNewShare), schdcache.DRS(targetOldShare)) < 0
 }
+
+func Enabled(pfs *config.FairSharing) bool {
+	return pfs != nil
+}
diff --git a/pkg/scheduler/preemption/preemption.go b/pkg/scheduler/preemption/preemption.go
index 96e6aca6e..625f63ce5 100644
--- a/pkg/scheduler/preemption/preemption.go
+++ b/pkg/scheduler/preemption/preemption.go
@@ -79,7 +79,7 @@ func New(
 	cl client.Client,
 	workloadOrdering workload.Ordering,
 	recorder record.EventRecorder,
-	fs config.FairSharing,
+	fs *config.FairSharing,
 	enabledAfs bool,
 	clock clock.Clock,
 ) *Preemptor {
@@ -88,8 +88,8 @@ func New(
 		client:            cl,
 		recorder:          recorder,
 		workloadOrdering:  workloadOrdering,
-		enableFairSharing: fs.Enable,
-		fsStrategies:      parseStrategies(fs.PreemptionStrategies),
+		enableFairSharing: fairsharing.Enabled(fs),
+		fsStrategies:      parseStrategies(fs),
 		enabledAfs:        enabledAfs,
 	}
 	return p
@@ -283,12 +283,12 @@ func restoreSnapshot(snapshot *schdcache.Snapshot, targets []*Target) {
 // parseStrategies converts an array of strategies into the functions to the used by the algorithm.
 // This function takes advantage of the properties of the preemption algorithm and the strategies.
 // The number of functions returned might not match the input slice.
-func parseStrategies(s []config.PreemptionStrategy) []fairsharing.Strategy {
-	if len(s) == 0 {
+func parseStrategies(fs *config.FairSharing) []fairsharing.Strategy {
+	if fs == nil || len(fs.PreemptionStrategies) == 0 {
 		return []fairsharing.Strategy{fairsharing.LessThanOrEqualToFinalShare, fairsharing.LessThanInitialShare}
 	}
-	strategies := make([]fairsharing.Strategy, len(s))
-	for i, strategy := range s {
+	strategies := make([]fairsharing.Strategy, len(fs.PreemptionStrategies))
+	for i, strategy := range fs.PreemptionStrategies {
 		switch strategy {
 		case config.LessThanOrEqualToFinalShare:
 			strategies[i] = fairsharing.LessThanOrEqualToFinalShare
diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go
index d7c8cae84..5b8fe45a3 100644
--- a/pkg/scheduler/scheduler.go
+++ b/pkg/scheduler/scheduler.go
@@ -47,6 +47,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/resources"
 	"sigs.k8s.io/kueue/pkg/scheduler/flavorassigner"
 	"sigs.k8s.io/kueue/pkg/scheduler/preemption"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/fairsharing"
 	afs "sigs.k8s.io/kueue/pkg/util/admissionfairsharing"
 	"sigs.k8s.io/kueue/pkg/util/api"
 	"sigs.k8s.io/kueue/pkg/util/priority"
@@ -75,7 +76,7 @@ type Scheduler struct {
 	admissionRoutineWrapper routine.Wrapper
 	preemptor               *preemption.Preemptor
 	workloadOrdering        workload.Ordering
-	fairSharing             config.FairSharing
+	fairSharing             *config.FairSharing
 	admissionFairSharing    *config.AdmissionFairSharing
 	clock                   clock.Clock
 
@@ -86,7 +87,7 @@ type Scheduler struct {
 
 type options struct {
 	podsReadyRequeuingTimestamp config.RequeuingTimestamp
-	fairSharing                 config.FairSharing
+	fairSharing                 *config.FairSharing
 	admissionFairSharing        *config.AdmissionFairSharing
 	clock                       clock.Clock
 }
@@ -110,7 +111,7 @@ func WithPodsReadyRequeuingTimestamp(ts config.RequeuingTimestamp) Option {
 func WithFairSharing(fs *config.FairSharing) Option {
 	return func(o *options) {
 		if fs != nil {
-			o.fairSharing = *fs
+			o.fairSharing = fs
 		}
 	}
 }
@@ -214,7 +215,7 @@ func (s *Scheduler) schedule(ctx context.Context) wait.SpeedSignal {
 	entries, inadmissibleEntries := s.nominate(ctx, headWorkloads, snapshot)
 
 	// 4. Create iterator which returns ordered entries.
-	iterator := makeIterator(ctx, entries, s.workloadOrdering, s.fairSharing.Enable)
+	iterator := makeIterator(ctx, entries, s.workloadOrdering, fairsharing.Enabled(s.fairSharing))
 
 	// 5. Admit entries, ensuring that no more than one workload gets
 	// admitted by a cohort (if borrowing).
@@ -533,7 +534,7 @@ func (s *Scheduler) getInitialAssignments(log logr.Logger, wl *workload.Info, sn
 
 	preemptionTargets, replaceableWorkloadSlice := workloadslicing.ReplacedWorkloadSlice(wl, snap)
 
-	flvAssigner := flavorassigner.New(wl, cq, snap.ResourceFlavors, s.fairSharing.Enable, preemption.NewOracle(s.preemptor, snap), replaceableWorkloadSlice)
+	flvAssigner := flavorassigner.New(wl, cq, snap.ResourceFlavors, fairsharing.Enabled(s.fairSharing), preemption.NewOracle(s.preemptor, snap), replaceableWorkloadSlice)
 	fullAssignment := flvAssigner.Assign(log, nil)
 
 	arm := fullAssignment.RepresentativeMode()
diff --git a/pkg/util/tas/tas_assignment.go b/pkg/util/tas/tas_assignment.go
new file mode 100644
index 000000000..985a940f2
--- /dev/null
+++ b/pkg/util/tas/tas_assignment.go
@@ -0,0 +1,257 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tas
+
+import (
+	"iter"
+	"slices"
+
+	"k8s.io/utils/ptr"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
+)
+
+type TopologyAssignment struct {
+	Levels  []string
+	Domains []TopologyDomainAssignment
+}
+
+type TopologyDomainAssignment struct {
+	Values []string
+	Count  int32
+}
+
+func valueAtIndex(values kueue.TopologyAssignmentSliceLevelValues, idx int) string {
+	if univ := values.Universal; univ != nil {
+		return *univ
+	}
+	ind := values.Individual
+	prefix := ptr.Deref(ind.CommonPrefix, "")
+	suffix := ptr.Deref(ind.CommonSuffix, "")
+	return prefix + ind.Roots[idx] + suffix
+}
+
+func countAtIndex(slice kueue.TopologyAssignmentSlice, idx int) int32 {
+	if univ := slice.PodCounts.Universal; univ != nil {
+		return *univ
+	}
+	return slice.PodCounts.Individual[idx]
+}
+
+func valuesAtLevel(ta *kueue.TopologyAssignment, levelIdx int) iter.Seq[string] {
+	return func(yield func(string) bool) {
+		for _, slice := range ta.Slices {
+			values := slice.ValuesPerLevel[levelIdx]
+			for i := range int(slice.DomainCount) {
+				if !yield(valueAtIndex(values, i)) {
+					return
+				}
+			}
+		}
+	}
+}
+
+func LowestLevelValues(ta *kueue.TopologyAssignment) iter.Seq[string] {
+	if ta == nil {
+		return nil
+	}
+	return valuesAtLevel(ta, len(ta.Levels)-1)
+}
+
+func PodCounts(ta *kueue.TopologyAssignment) iter.Seq[int32] {
+	if ta == nil {
+		return nil
+	}
+	return func(yield func(int32) bool) {
+		for _, slice := range ta.Slices {
+			for i := range int(slice.DomainCount) {
+				if !yield(countAtIndex(slice, i)) {
+					return
+				}
+			}
+		}
+	}
+}
+
+func TotalDomainCount(ta *kueue.TopologyAssignment) int {
+	if ta == nil {
+		return 0
+	}
+	res := 0
+	for _, slice := range ta.Slices {
+		res += int(slice.DomainCount)
+	}
+	return res
+}
+
+func InternalSeqFrom(ta *kueue.TopologyAssignment) iter.Seq[TopologyDomainAssignment] {
+	if ta == nil {
+		return nil
+	}
+	return func(yield func(TopologyDomainAssignment) bool) {
+		for _, slice := range ta.Slices {
+			for i := range int(slice.DomainCount) {
+				req := TopologyDomainAssignment{
+					Count:  countAtIndex(slice, i),
+					Values: make([]string, 0, len(ta.Levels)),
+				}
+				for levelIdx := range ta.Levels {
+					req.Values = append(req.Values, valueAtIndex(slice.ValuesPerLevel[levelIdx], i))
+				}
+				if !yield(req) {
+					return
+				}
+			}
+		}
+	}
+}
+
+func InternalFrom(ta *kueue.TopologyAssignment) *TopologyAssignment {
+	if ta == nil {
+		return nil
+	}
+	return &TopologyAssignment{
+		Levels:  ta.Levels,
+		Domains: slices.Collect(InternalSeqFrom(ta)),
+	}
+}
+
+func fillSingleCompactSliceValues(
+	values *kueue.TopologyAssignmentSliceLevelValues,
+	inputProvider func() iter.Seq[string],
+) {
+	var prefix, suffix string
+	var maxLen, minLen, count int
+	for s := range inputProvider() {
+		count++
+		if count == 1 {
+			prefix = s
+			suffix = s
+			maxLen = len(s)
+			minLen = len(s)
+		} else {
+			n := len(s)
+			minLen = min(minLen, n)
+			maxLen = max(maxLen, n)
+			if n < len(prefix) {
+				prefix = prefix[:n]
+			}
+			if n < len(suffix) {
+				suffix = suffix[len(suffix)-n:]
+			}
+			for i := 0; i < len(prefix); i++ {
+				if s[i] != prefix[i] {
+					prefix = prefix[:i]
+					break
+				}
+			}
+			for i := 0; i < len(suffix); i++ {
+				if s[len(s)-1-i] != suffix[len(suffix)-1-i] {
+					suffix = suffix[len(suffix)-i:]
+					break
+				}
+			}
+		}
+	}
+
+	// All strings equal
+	if len(prefix) == maxLen {
+		values.Universal = &prefix
+		return
+	}
+
+	// Ensure that common prefix & suffix don't overlap.
+	// (Motivating example: {"ababa", "aba"})
+	if len(prefix)+len(suffix) > minLen {
+		prefix = prefix[:minLen-len(suffix)]
+	}
+
+	ind := &kueue.TopologyAssignmentSliceLevelIndividualValues{}
+	values.Individual = ind
+	if len(prefix) > 0 {
+		ind.CommonPrefix = &prefix
+	}
+	if len(suffix) > 0 {
+		ind.CommonSuffix = &suffix
+	}
+	values.Individual.Roots = make([]string, 0, count)
+	for s := range inputProvider() {
+		ind.Roots = append(ind.Roots, s[len(prefix):len(s)-len(suffix)])
+	}
+}
+
+// singleCompactSliceEncoding translates a v1beta1 TopologyAssignment
+// to a v1beta2 counterpart consisting of a single slice,
+// in which the "compressing options" (CommonPrefix, CommonSuffix,
+// Universal values for placement labels as well as Pod counts)
+// are used as much as possible.
+func singleCompactSliceEncoding(ta *TopologyAssignment) *kueue.TopologyAssignment {
+	n := len(ta.Domains)
+	if n == 0 {
+		return &kueue.TopologyAssignment{
+			Levels: ta.Levels,
+			Slices: []kueue.TopologyAssignmentSlice{},
+		}
+	}
+
+	levelCount := len(ta.Levels)
+	slice := &kueue.TopologyAssignmentSlice{
+		DomainCount:    int32(n),
+		ValuesPerLevel: make([]kueue.TopologyAssignmentSliceLevelValues, levelCount),
+	}
+
+	for i := range levelCount {
+		levelValuesProvider := func() iter.Seq[string] {
+			return func(yield func(string) bool) {
+				for j := range n {
+					if !yield(ta.Domains[j].Values[i]) {
+						return
+					}
+				}
+			}
+		}
+		fillSingleCompactSliceValues(&slice.ValuesPerLevel[i], levelValuesProvider)
+	}
+
+	podCounts := make([]int32, 0, n)
+	samePodCounts := true
+	for i := range n {
+		podCounts = append(podCounts, ta.Domains[i].Count)
+		if i > 0 && ta.Domains[i].Count != ta.Domains[i-1].Count {
+			samePodCounts = false
+		}
+	}
+	if samePodCounts {
+		slice.PodCounts.Universal = &podCounts[0]
+	} else {
+		slice.PodCounts.Individual = podCounts
+	}
+	return &kueue.TopologyAssignment{
+		Levels: ta.Levels,
+		Slices: []kueue.TopologyAssignmentSlice{*slice},
+	}
+}
+
+// V1Beta2From translates a v1beta1 TopologyAssignment into the v1beta2 format.
+// The choice of a specific v1beta2 representation is an implementation detail,
+// which may change in the future. (See KEP-2724).
+func V1Beta2From(ta *TopologyAssignment) *kueue.TopologyAssignment {
+	if ta == nil {
+		return nil
+	}
+	return singleCompactSliceEncoding(ta)
+}
diff --git a/pkg/workload/workload.go b/pkg/workload/workload.go
index 4375b16db..c2e95af32 100644
--- a/pkg/workload/workload.go
+++ b/pkg/workload/workload.go
@@ -51,6 +51,7 @@ import (
 	utilptr "sigs.k8s.io/kueue/pkg/util/ptr"
 	utilqueue "sigs.k8s.io/kueue/pkg/util/queue"
 	utilslices "sigs.k8s.io/kueue/pkg/util/slices"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	"sigs.k8s.io/kueue/pkg/util/wait"
 )
 
@@ -532,11 +533,11 @@ func totalRequestsFromAdmission(wl *kueue.Workload) []PodSetResources {
 			setRes.TopologyRequest = &TopologyRequest{
 				Levels: psa.TopologyAssignment.Levels,
 			}
-			for _, domain := range psa.TopologyAssignment.Domains {
+			for req := range tas.InternalSeqFrom(psa.TopologyAssignment) {
 				setRes.TopologyRequest.DomainRequests = append(setRes.TopologyRequest.DomainRequests, TopologyDomainRequests{
-					Values:            domain.Values,
+					Values:            req.Values,
 					SinglePodRequests: setRes.SinglePodRequests(),
-					Count:             domain.Count,
+					Count:             req.Count,
 				})
 			}
 		}
@@ -1182,8 +1183,8 @@ func HasTopologyAssignmentWithUnhealthyNode(w *kueue.Workload) bool {
 		if psa.TopologyAssignment == nil {
 			continue
 		}
-		for _, domain := range psa.TopologyAssignment.Domains {
-			if HasUnhealthyNode(w, domain.Values[len(domain.Values)-1]) {
+		for value := range tas.LowestLevelValues(psa.TopologyAssignment) {
+			if HasUnhealthyNode(w, value) {
 				return true
 			}
 		}
diff --git a/site/content/en/docs/concepts/preemption.md b/site/content/en/docs/concepts/preemption.md
index 29fe0874d..255fa83c9 100644
--- a/site/content/en/docs/concepts/preemption.md
+++ b/site/content/en/docs/concepts/preemption.md
@@ -138,7 +138,6 @@ To enable Fair Sharing, [use a Kueue Configuration](/docs/installation#install-a
 apiVersion: config.kueue.x-k8s.io/v1beta2
 kind: Configuration
 fairSharing:
-  enable: true
   preemptionStrategies: [LessThanOrEqualToFinalShare, LessThanInitialShare]
 ```
 
diff --git a/site/content/en/docs/reference/kueue-config.v1beta2.md b/site/content/en/docs/reference/kueue-config.v1beta2.md
index d08e87494..5194eb905 100644
--- a/site/content/en/docs/reference/kueue-config.v1beta2.md
+++ b/site/content/en/docs/reference/kueue-config.v1beta2.md
@@ -373,14 +373,6 @@ The total length of each name must not exceed 253 characters.</p>
 <tbody>
     
   
-<tr><td><code>enable</code> <B>[Required]</B><br/>
-<code>bool</code>
-</td>
-<td>
-   <p>enable indicates whether to enable Fair Sharing for all cohorts.
-Defaults to false.</p>
-</td>
-</tr>
 <tr><td><code>preemptionStrategies</code> <B>[Required]</B><br/>
 <a href="#config-kueue-x-k8s-io-v1beta2-PreemptionStrategy"><code>[]PreemptionStrategy</code></a>
 </td>
diff --git a/site/content/en/docs/reference/kueue.v1beta2.md b/site/content/en/docs/reference/kueue.v1beta2.md
index 4a061fcd5..05cc1e6ea 100644
--- a/site/content/en/docs/reference/kueue.v1beta2.md
+++ b/site/content/en/docs/reference/kueue.v1beta2.md
@@ -1912,46 +1912,144 @@ in that case spec.podSets[*].count value will be used.</p>
 topology domains corresponding to the lowest level of the topology.
 The assignment specifies the number of Pods to be scheduled per topology
 domain and specifies the node selectors for each topology domain, in the
-following way: the node selector keys are specified by the levels field
-(same for all domains), and the corresponding node selector value is
-specified by the domains.values subfield. If the TopologySpec.Levels field contains
-&quot;kubernetes.io/hostname&quot; label, topologyAssignment will contain data only for
-this label, and omit higher levels in the topology</p>
-<p>Example:</p>
+following way:</p>
+<ul>
+<li><code>levels</code> specifies the node selector keys (same for all domains).
+<ul>
+<li>If the TopologySpec.Levels field contains &quot;kubernetes.io/hostname&quot; label,
+topologyAssignment will contain data only for this label,
+and omit higher levels in the topology.</li>
+</ul>
+</li>
+<li><code>slices</code> specifies the node selector values and pod counts for all domains
+(which may be partitioned into separate slices).
+<ul>
+<li>The node selector values are arranged first by topology level, only then by domain.
+(This allows &quot;optimizing&quot; similar values; see below).</li>
+</ul>
+</li>
+<li>The format of <code>slices</code> supports the following variations
+(aimed to optimize the total bytesize for very large number of domains; see examples below):
+<ul>
+<li>When all node selector values (at a given topology level, in a given slice)
+share a common prefix and/or suffix, these may be stored
+in dedicated <code>commonPrefix</code>/<code>commonSuffix</code> fields.
+If so, the array of <code>roots</code> will only store the remaining parts of these strings.</li>
+<li>When all node selector values (at a given topology level, in a given slice)
+are identical, this may be represented by <code>universal</code> value.</li>
+<li>When all pod counts (in a given slice) are identical,
+this may be represented by <code>universal</code> pod count.</li>
+</ul>
+</li>
+</ul>
+<p>Example 1:</p>
+<p>The following represents an assignment in which:</p>
+<ul>
+<li>4 Pods are to be scheduled on nodes matching the node selector:
+<ul>
+<li>cloud.provider.com/topology-block: block-1</li>
+<li>cloud.provider.com/topology-rack: rack-1</li>
+</ul>
+</li>
+<li>2 Pods are to be scheduled on nodes matching the node selector:
+<ul>
+<li>cloud.provider.com/topology-block: block-1</li>
+<li>cloud.provider.com/topology-rack: rack-2</li>
+</ul>
+</li>
+</ul>
 <p>topologyAssignment:
 levels:</p>
 <ul>
 <li>cloud.provider.com/topology-block</li>
 <li>cloud.provider.com/topology-rack
-domains:</li>
-<li>values: [block-1, rack-1]
-count: 4</li>
-<li>values: [block-1, rack-2]
-count: 2</li>
+slices:</li>
+<li>domainCount: 2
+valuesPerLevel:
+<ul>
+<li>individual:
+roots: [block-1, block-1]</li>
+<li>individual:
+roots: [rack-1, rack-2]
+podCounts:
+individual: [4, 2]</li>
+</ul>
+</li>
 </ul>
-<p>Here:</p>
+<p>Example 2:</p>
+<p>The following is equivalent to Example 1 - but using extracted prefix and universalValue.</p>
+<p>topologyAssignment:
+levels:</p>
 <ul>
-<li>4 Pods are to be scheduled on nodes matching the node selector:
-cloud.provider.com/topology-block: block-1
-cloud.provider.com/topology-rack: rack-1</li>
-<li>2 Pods are to be scheduled on nodes matching the node selector:
-cloud.provider.com/topology-block: block-1
-cloud.provider.com/topology-rack: rack-2</li>
+<li>cloud.provider.com/topology-block</li>
+<li>cloud.provider.com/topology-rack
+slices:</li>
+<li>domainCount: 2
+valuesPerLevel:
+<ul>
+<li>universal: block-1</li>
+<li>individual:
+prefix: rack-
+roots: [1, 2]
+podCounts:
+individual: [4, 2]</li>
+</ul>
+</li>
+</ul>
+<p>Example 3:</p>
+<p>Now suppose that:</p>
+<ul>
+<li>the Topology object defines kubernetes.io/hostname as the lowest level
+(and hence, in the topologyAssignment, we omit all other levels
+since the hostname label suffices to explicitly identify a proper node),</li>
+<li>we assign 1 Pod per each node,</li>
+<li>the node naming scheme is <code>block-{blockId}-rack-{rackId}-node-{nodeId}</code>.
+Then, using the &quot;extraction of commons&quot;, the assignment from Examples 1-2 would look as follows:</li>
 </ul>
-<p>Example:
-Below there is an equivalent of the above example assuming, Topology
-object defines kubernetes.io/hostname as the lowest level in topology.
-Hence we omit higher level of topologies, since the hostname label
-is sufficient to explicitly identify a proper node.</p>
 <p>topologyAssignment:
 levels:</p>
 <ul>
 <li>kubernetes.io/hostname
-domains:</li>
-<li>values: [hostname-1]
-count: 4</li>
-<li>values: [hostname-2]
-count: 2</li>
+slices:</li>
+<li>domainCount: 6
+valuesPerLevel:
+<ul>
+<li>individual:
+prefix: block-1-rack-
+roots: [1-node-1, 1-node-2, 1-node-3, 1-node-4, 2-node-1, 2-node-2]
+podCounts:
+universal: 1</li>
+</ul>
+</li>
+</ul>
+<p>Example 4:</p>
+<p>By using multiple slices, we can afford even longer common prefixes.
+The assignment from Example 3 can be alternatively represented as follows:</p>
+<p>topologyAssignment:
+levels:</p>
+<ul>
+<li>kubernetes.io/hostname
+slices:</li>
+<li>domainCount: 4
+valuesPerLevel:
+<ul>
+<li>individual:
+prefix: block-1-rack-1-node-
+roots: [1, 2, 3, 4]
+podCounts:
+universal: 1</li>
+</ul>
+</li>
+<li>domainCount: 2
+valuesPerLevel:
+<ul>
+<li>individual:
+prefix: block-1-rack-2-node-
+roots: [1, 2]
+podCounts:
+universal: 1</li>
+</ul>
+</li>
 </ul>
 </td>
 </tr>
@@ -2859,18 +2957,18 @@ topology (i.e. node label keys), from the highest to the lowest level of
 the topology.</p>
 </td>
 </tr>
-<tr><td><code>domains</code> <B>[Required]</B><br/>
-<a href="#kueue-x-k8s-io-v1beta2-TopologyDomainAssignment"><code>[]TopologyDomainAssignment</code></a>
+<tr><td><code>slices</code> <B>[Required]</B><br/>
+<a href="#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlice"><code>[]TopologyAssignmentSlice</code></a>
 </td>
 <td>
-   <p>domains is a list of topology assignments split by topology domains at
-the lowest level of the topology.</p>
+   <p>slices represent topology assignments for subsets of pods of a workload.
+The full assignment is obtained as a union of all slices.</p>
 </td>
 </tr>
 </tbody>
 </table>
 
-## `TopologyDomainAssignment`     {#kueue-x-k8s-io-v1beta2-TopologyDomainAssignment}
+## `TopologyAssignmentSlice`     {#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlice}
     
 
 **Appears in:**
@@ -2884,21 +2982,136 @@ the lowest level of the topology.</p>
 <tbody>
     
   
-<tr><td><code>values</code> <B>[Required]</B><br/>
+<tr><td><code>domainCount</code> <B>[Required]</B><br/>
+<code>int32</code>
+</td>
+<td>
+   <p>domainCount is the number of domains covered by this slice.</p>
+</td>
+</tr>
+<tr><td><code>valuesPerLevel</code> <B>[Required]</B><br/>
+<a href="#kueue-x-k8s-io-v1beta2-TopologyAssignmentSliceLevelValues"><code>[]TopologyAssignmentSliceLevelValues</code></a>
+</td>
+<td>
+   <p>valuesPerLevel has one entry for each of the Levels specified in the TopologyAssignment.
+The entry corresponding to a particular level specifies the placement of pods at that level.</p>
+</td>
+</tr>
+<tr><td><code>podCounts</code> <B>[Required]</B><br/>
+<a href="#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlicePodCounts"><code>TopologyAssignmentSlicePodCounts</code></a>
+</td>
+<td>
+   <p>podCounts specifies the number of pods allocated per each domain.</p>
+</td>
+</tr>
+</tbody>
+</table>
+
+## `TopologyAssignmentSliceLevelIndividualValues`     {#kueue-x-k8s-io-v1beta2-TopologyAssignmentSliceLevelIndividualValues}
+    
+
+**Appears in:**
+
+- [TopologyAssignmentSliceLevelValues](#kueue-x-k8s-io-v1beta2-TopologyAssignmentSliceLevelValues)
+
+
+
+<table class="table">
+<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
+<tbody>
+    
+  
+<tr><td><code>commonPrefix</code><br/>
+<code>string</code>
+</td>
+<td>
+   <p>commonPrefix specifies a common prefix for all values in this slice assignment.
+It must be either nil pointer or a non-empty string.</p>
+</td>
+</tr>
+<tr><td><code>commonSuffix</code><br/>
+<code>string</code>
+</td>
+<td>
+   <p>commonSuffix specifies a common suffix for all values in this slice assignment.
+It must be either nil pointer or a non-empty string.</p>
+</td>
+</tr>
+<tr><td><code>roots</code> <B>[Required]</B><br/>
 <code>[]string</code>
 </td>
 <td>
-   <p>values is an ordered list of node selector values describing a topology
-domain. The values correspond to the consecutive topology levels, from
-the highest to the lowest.</p>
+   <p>roots specifies the values in this assignment (excluding commonPrefix and commonSuffix, if non-empty).
+Its length must be equal to the &quot;domainCount&quot; field of the TopologyAssignmentSlice.</p>
 </td>
 </tr>
-<tr><td><code>count</code> <B>[Required]</B><br/>
+</tbody>
+</table>
+
+## `TopologyAssignmentSliceLevelValues`     {#kueue-x-k8s-io-v1beta2-TopologyAssignmentSliceLevelValues}
+    
+
+**Appears in:**
+
+- [TopologyAssignmentSlice](#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlice)
+
+
+
+<table class="table">
+<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
+<tbody>
+    
+  
+<tr><td><code>universal</code><br/>
+<code>string</code>
+</td>
+<td>
+   <p>universal - if set - specifies a single topology placement value (at a particular topology level)
+that applies to all pods in the current TopologyAssignmentSlice.
+Exactly one of universal, individual must be set.</p>
+</td>
+</tr>
+<tr><td><code>individual</code><br/>
+<a href="#kueue-x-k8s-io-v1beta2-TopologyAssignmentSliceLevelIndividualValues"><code>TopologyAssignmentSliceLevelIndividualValues</code></a>
+</td>
+<td>
+   <p>individual - if set - specifies multiple topology placement values (at a particular topology level)
+that apply to the pods in the current TopologyAssignmentSlice.
+Exactly one of universal, individual must be set.</p>
+</td>
+</tr>
+</tbody>
+</table>
+
+## `TopologyAssignmentSlicePodCounts`     {#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlicePodCounts}
+    
+
+**Appears in:**
+
+- [TopologyAssignmentSlice](#kueue-x-k8s-io-v1beta2-TopologyAssignmentSlice)
+
+
+
+<table class="table">
+<thead><tr><th width="30%">Field</th><th>Description</th></tr></thead>
+<tbody>
+    
+  
+<tr><td><code>universal</code><br/>
 <code>int32</code>
 </td>
 <td>
-   <p>count indicates the number of Pods to be scheduled in the topology
-domain indicated by the values field.</p>
+   <p>universal - if set - specifies the number of pods allocated in every domain in this slice.
+Exactly one of universal, individual must be set.</p>
+</td>
+</tr>
+<tr><td><code>individual</code><br/>
+<code>[]int32</code>
+</td>
+<td>
+   <p>individual - if set - specifies the number of pods allocated in each domain in this slice.
+If set, its length must be equal to the &quot;domainCount&quot; field of the TopologyAssignmentSlice.
+Exactly one of universal, individual must be set.</p>
 </td>
 </tr>
 </tbody>
diff --git a/site/content/zh-CN/docs/concepts/preemption.md b/site/content/zh-CN/docs/concepts/preemption.md
index 3b6490fe4..1dd8db55b 100644
--- a/site/content/zh-CN/docs/concepts/preemption.md
+++ b/site/content/zh-CN/docs/concepts/preemption.md
@@ -104,7 +104,6 @@ Kueue 提供了两种抢占算法。它们的主要区别在于：当抢占 Clus
 apiVersion: config.kueue.x-k8s.io/v1beta2
 kind: Configuration
 fairSharing:
-  enable: true
   preemptionStrategies: [LessThanOrEqualToFinalShare, LessThanInitialShare]
 ```
 
diff --git a/site/static/examples/jobs/ray-cluster-sample.yaml b/site/static/examples/jobs/ray-cluster-sample.yaml
index 5838b31e3..eb2da94a7 100644
--- a/site/static/examples/jobs/ray-cluster-sample.yaml
+++ b/site/static/examples/jobs/ray-cluster-sample.yaml
@@ -53,9 +53,9 @@ spec:
                 cpu: "1"
                 memory: "2G"
               requests:
-                # For production use-cases, we recommend specifying integer CPU reqests and limits.
+                # For production use-cases, we recommend specifying integer CPU requests and limits.
                 # We also recommend setting requests equal to limits for both CPU and memory.
-                # For this example, we use a 500m CPU request to accomodate resource-constrained local
+                # For this example, we use a 500m CPU request to accommodate resource-constrained local
                 # Kubernetes testing environments such as KinD and minikube.
                 cpu: "1"
                 memory: "2G"
@@ -108,14 +108,14 @@ spec:
                 limits:
                   cpu: "1"
                   memory: "1G"
-                # For production use-cases, we recommend specifying integer CPU reqests and limits.
+                # For production use-cases, we recommend specifying integer CPU requests and limits.
                 # We also recommend setting requests equal to limits for both CPU and memory.
-                # For this example, we use a 500m CPU request to accomodate resource-constrained local
+                # For this example, we use a 500m CPU request to accommodate resource-constrained local
                 # Kubernetes testing environments such as KinD and minikube.
                 requests:
-                  # For production use-cases, we recommend specifying integer CPU reqests and limits.
+                  # For production use-cases, we recommend specifying integer CPU requests and limits.
                   # We also recommend setting requests equal to limits for both CPU and memory.
-                  # For this example, we use a 500m CPU request to accomodate resource-constrained local
+                  # For this example, we use a 500m CPU request to accommodate resource-constrained local
                   # Kubernetes testing environments such as KinD and minikube.
                   cpu: "1"
                   # For production use-cases, we recommend allocating at least 8Gb memory for each Ray container.



Test patch:
diff --git a/apis/config/v1beta1/configuration_conversion_test.go b/apis/config/v1beta1/configuration_conversion_test.go
new file mode 100644
index 000000000..b0254aa81
--- /dev/null
+++ b/apis/config/v1beta1/configuration_conversion_test.go
@@ -0,0 +1,149 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+
+	"sigs.k8s.io/kueue/apis/config/v1beta2"
+)
+
+func TestConfigurationQueueConvertTo(t *testing.T) {
+	testCases := map[string]struct {
+		input    *Configuration
+		expected *v1beta2.Configuration
+	}{
+		"minimal Configuration": {
+			input:    &Configuration{},
+			expected: &v1beta2.Configuration{},
+		},
+		"with FairSharing": {
+			input: &Configuration{
+				FairSharing: &FairSharing{
+					Enable:               true,
+					PreemptionStrategies: []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare},
+				},
+			},
+			expected: &v1beta2.Configuration{
+				FairSharing: &v1beta2.FairSharing{
+					PreemptionStrategies: []v1beta2.PreemptionStrategy{v1beta2.LessThanOrEqualToFinalShare, v1beta2.LessThanInitialShare},
+				},
+			},
+		},
+		"with FairSharing disabled": {
+			input: &Configuration{
+				FairSharing: &FairSharing{
+					Enable:               false,
+					PreemptionStrategies: []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare},
+				},
+			},
+			expected: &v1beta2.Configuration{
+				FairSharing: nil,
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			result := &v1beta2.Configuration{}
+			if err := tc.input.ConvertTo(result); err != nil {
+				t.Fatalf("ConvertTo failed: %v", err)
+			}
+			if diff := cmp.Diff(tc.expected, result); diff != "" {
+				t.Errorf("unexpected conversion result (-want +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConfigurationQueueConvertFrom(t *testing.T) {
+	testCases := map[string]struct {
+		input    *v1beta2.Configuration
+		expected *Configuration
+	}{
+		"minimal Configuration": {
+			input:    &v1beta2.Configuration{},
+			expected: &Configuration{},
+		},
+		"with FairSharing": {
+			input: &v1beta2.Configuration{
+				FairSharing: &v1beta2.FairSharing{
+					PreemptionStrategies: []v1beta2.PreemptionStrategy{v1beta2.LessThanOrEqualToFinalShare, v1beta2.LessThanInitialShare},
+				},
+			},
+			expected: &Configuration{
+				FairSharing: &FairSharing{
+					Enable:               true,
+					PreemptionStrategies: []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare},
+				},
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			result := &Configuration{}
+			if err := result.ConvertFrom(tc.input); err != nil {
+				t.Fatalf("ConvertFrom failed: %v", err)
+			}
+			if diff := cmp.Diff(tc.expected, result); diff != "" {
+				t.Errorf("unexpected conversion result (-want +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConfigurationQueueConversion_RoundTrip(t *testing.T) {
+	testCases := map[string]struct {
+		v1beta1Obj *Configuration
+	}{
+		"minimal Configuration": {
+			v1beta1Obj: &Configuration{},
+		},
+		"with FairSharing": {
+			v1beta1Obj: &Configuration{
+				FairSharing: &FairSharing{
+					Enable:               true,
+					PreemptionStrategies: []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare},
+				},
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			// Convert v1beta1 -> v1beta2
+			v1beta2Obj := &v1beta2.Configuration{}
+			if err := tc.v1beta1Obj.ConvertTo(v1beta2Obj); err != nil {
+				t.Fatalf("ConvertTo failed: %v", err)
+			}
+
+			// Convert v1beta2 -> v1beta1 (round-trip)
+			roundTripped := &Configuration{}
+			if err := roundTripped.ConvertFrom(v1beta2Obj); err != nil {
+				t.Fatalf("ConvertFrom failed: %v", err)
+			}
+
+			// Verify round-trip
+			if diff := cmp.Diff(tc.v1beta1Obj, roundTripped); diff != "" {
+				t.Errorf("round-trip conversion produced diff (-original +roundtripped):\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/apis/config/v1beta2/defaults_test.go b/apis/config/v1beta2/defaults_test.go
index db28943f0..9a28ed75f 100644
--- a/apis/config/v1beta2/defaults_test.go
+++ b/apis/config/v1beta2/defaults_test.go
@@ -565,9 +565,7 @@ func TestSetDefaults_Configuration(t *testing.T) {
 				InternalCertManagement: &InternalCertManagement{
 					Enable: ptr.To(false),
 				},
-				FairSharing: &FairSharing{
-					Enable: true,
-				},
+				FairSharing: &FairSharing{},
 			},
 			want: &Configuration{
 				Namespace:         ptr.To(DefaultNamespace),
@@ -580,7 +578,6 @@ func TestSetDefaults_Configuration(t *testing.T) {
 				MultiKueue:                   defaultMultiKueue,
 				ManagedJobsNamespaceSelector: defaultManagedJobsNamespaceSelector,
 				FairSharing: &FairSharing{
-					Enable:               true,
 					PreemptionStrategies: []PreemptionStrategy{LessThanOrEqualToFinalShare, LessThanInitialShare},
 				},
 				WaitForPodsReady: &WaitForPodsReady{},
diff --git a/apis/kueue/v1beta1/workload_conversion_test.go b/apis/kueue/v1beta1/workload_conversion_test.go
index e9b9bdd0a..94421acf5 100644
--- a/apis/kueue/v1beta1/workload_conversion_test.go
+++ b/apis/kueue/v1beta1/workload_conversion_test.go
@@ -148,6 +148,65 @@ func TestWorkloadConvertTo(t *testing.T) {
 				},
 			},
 		},
+		"with TopologyAssignment": {
+			input: &Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: WorkloadStatus{
+					Admission: &Admission{
+						PodSetAssignments: []PodSetAssignment{
+							{
+								TopologyAssignment: &TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Domains: []TopologyDomainAssignment{
+										{
+											Values: []string{"a1", "b1"},
+											Count:  10,
+										},
+										{
+											Values: []string{"a1", "b2"},
+											Count:  20,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expected: &v1beta2.Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: v1beta2.WorkloadStatus{
+					Admission: &v1beta2.Admission{
+						PodSetAssignments: []v1beta2.PodSetAssignment{
+							{
+								TopologyAssignment: &v1beta2.TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Slices: []v1beta2.TopologyAssignmentSlice{
+										{
+											DomainCount: 2,
+											ValuesPerLevel: []v1beta2.TopologyAssignmentSliceLevelValues{
+												{
+													Universal: ptr.To("a1"),
+												},
+												{
+													Individual: &v1beta2.TopologyAssignmentSliceLevelIndividualValues{
+														CommonPrefix: ptr.To("b"),
+														Roots:        []string{"1", "2"},
+													},
+												},
+											},
+											PodCounts: v1beta2.TopologyAssignmentSlicePodCounts{
+												Individual: []int32{10, 20},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for name, tc := range testCases {
@@ -285,6 +344,149 @@ func TestWorkloadConvertFrom(t *testing.T) {
 				},
 			},
 		},
+		"with TopologyAssignment": {
+			input: &v1beta2.Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: v1beta2.WorkloadStatus{
+					Admission: &v1beta2.Admission{
+						PodSetAssignments: []v1beta2.PodSetAssignment{
+							{
+								TopologyAssignment: &v1beta2.TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Slices: []v1beta2.TopologyAssignmentSlice{
+										{
+											DomainCount: 2,
+											ValuesPerLevel: []v1beta2.TopologyAssignmentSliceLevelValues{
+												{
+													Universal: ptr.To("a1"),
+												},
+												{
+													Individual: &v1beta2.TopologyAssignmentSliceLevelIndividualValues{
+														CommonPrefix: ptr.To("b"),
+														Roots:        []string{"1", "2"},
+													},
+												},
+											},
+											PodCounts: v1beta2.TopologyAssignmentSlicePodCounts{
+												Individual: []int32{10, 20},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expected: &Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: WorkloadStatus{
+					Admission: &Admission{
+						PodSetAssignments: []PodSetAssignment{
+							{
+								TopologyAssignment: &TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Domains: []TopologyDomainAssignment{
+										{
+											Values: []string{"a1", "b1"},
+											Count:  10,
+										},
+										{
+											Values: []string{"a1", "b2"},
+											Count:  20,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"with TopologyAssignment, multiple slices": {
+			input: &v1beta2.Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: v1beta2.WorkloadStatus{
+					Admission: &v1beta2.Admission{
+						PodSetAssignments: []v1beta2.PodSetAssignment{
+							{
+								TopologyAssignment: &v1beta2.TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Slices: []v1beta2.TopologyAssignmentSlice{
+										{
+											DomainCount: 2,
+											ValuesPerLevel: []v1beta2.TopologyAssignmentSliceLevelValues{
+												{
+													Universal: ptr.To("a1"),
+												},
+												{
+													Individual: &v1beta2.TopologyAssignmentSliceLevelIndividualValues{
+														CommonPrefix: ptr.To("b"),
+														Roots:        []string{"1", "2"},
+													},
+												},
+											},
+											PodCounts: v1beta2.TopologyAssignmentSlicePodCounts{
+												Individual: []int32{10, 20},
+											},
+										},
+										{
+											DomainCount: 2,
+											ValuesPerLevel: []v1beta2.TopologyAssignmentSliceLevelValues{
+												{
+													Universal: ptr.To("a2"),
+												},
+												{
+													Individual: &v1beta2.TopologyAssignmentSliceLevelIndividualValues{
+														Roots:        []string{"first", "second"},
+														CommonSuffix: ptr.To("-in-a2"),
+													},
+												},
+											},
+											PodCounts: v1beta2.TopologyAssignmentSlicePodCounts{
+												Universal: ptr.To(int32(50)),
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expected: &Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: WorkloadStatus{
+					Admission: &Admission{
+						PodSetAssignments: []PodSetAssignment{
+							{
+								TopologyAssignment: &TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Domains: []TopologyDomainAssignment{
+										{
+											Values: []string{"a1", "b1"},
+											Count:  10,
+										},
+										{
+											Values: []string{"a1", "b2"},
+											Count:  20,
+										},
+										{
+											Values: []string{"a2", "first-in-a2"},
+											Count:  50,
+										},
+										{
+											Values: []string{"a2", "second-in-a2"},
+											Count:  50,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for name, tc := range testCases {
@@ -360,6 +562,32 @@ func TestWorkloadConversion_RoundTrip(t *testing.T) {
 				},
 			},
 		},
+		"Workload with TopologyAssignment": {
+			v1beta1Obj: &Workload{
+				ObjectMeta: defaultObjectMeta,
+				Status: WorkloadStatus{
+					Admission: &Admission{
+						PodSetAssignments: []PodSetAssignment{
+							{
+								TopologyAssignment: &TopologyAssignment{
+									Levels: []string{"a", "b"},
+									Domains: []TopologyDomainAssignment{
+										{
+											Values: []string{"a1", "b1"},
+											Count:  10,
+										},
+										{
+											Values: []string{"a1", "b2"},
+											Count:  20,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for name, tc := range testCases {
diff --git a/pkg/cache/queue/cluster_queue_test.go b/pkg/cache/queue/cluster_queue_test.go
index 51839ec45..a67cdaa61 100644
--- a/pkg/cache/queue/cluster_queue_test.go
+++ b/pkg/cache/queue/cluster_queue_test.go
@@ -210,7 +210,7 @@ func Test_Pop(t *testing.T) {
 }
 
 func Test_Delete(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	cq := newClusterQueueImpl(ctx, nil, defaultOrdering, testingclock.NewFakeClock(time.Now()), nil, false, nil)
 	wl1 := utiltestingapi.MakeWorkload("workload-1", defaultNamespace).Obj()
 	wl2 := utiltestingapi.MakeWorkload("workload-2", defaultNamespace).Obj()
@@ -219,13 +219,13 @@ func Test_Delete(t *testing.T) {
 	if cq.PendingTotal() != 2 {
 		t.Error("ClusterQueue should have two workload")
 	}
-	cq.Delete(wl1)
+	cq.Delete(log, wl1)
 	if cq.PendingTotal() != 1 {
 		t.Error("ClusterQueue should have only one workload")
 	}
 	// Change workload item, ClusterQueue.Delete should only care about the namespace and name.
 	wl2.Spec = kueue.WorkloadSpec{QueueName: "default"}
-	cq.Delete(wl2)
+	cq.Delete(log, wl2)
 	if cq.PendingTotal() != 0 {
 		t.Error("ClusterQueue should have be empty")
 	}
@@ -245,7 +245,7 @@ func Test_Info(t *testing.T) {
 }
 
 func Test_AddFromLocalQueue(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	cq := newClusterQueueImpl(ctx, nil, defaultOrdering, testingclock.NewFakeClock(time.Now()), nil, false, nil)
 	wl := utiltestingapi.MakeWorkload("workload-1", defaultNamespace).Obj()
 	queue := &LocalQueue{
@@ -257,14 +257,14 @@ func Test_AddFromLocalQueue(t *testing.T) {
 	if added := cq.AddFromLocalQueue(queue); added {
 		t.Error("expected workload not to be added")
 	}
-	cq.Delete(wl)
+	cq.Delete(log, wl)
 	if added := cq.AddFromLocalQueue(queue); !added {
 		t.Error("workload should be added to the ClusterQueue")
 	}
 }
 
 func Test_DeleteFromLocalQueue(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	cq := newClusterQueueImpl(ctx, nil, defaultOrdering, testingclock.NewFakeClock(time.Now()), nil, false, nil)
 	q := utiltestingapi.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj()
 	qImpl := newLocalQueue(q)
@@ -295,7 +295,7 @@ func Test_DeleteFromLocalQueue(t *testing.T) {
 		t.Errorf("clusterQueue's workload number in inadmissibleWorkloads not right, want %v, got %v", len(inadmissibleWorkloads), len(cq.inadmissibleWorkloads))
 	}
 
-	cq.DeleteFromLocalQueue(qImpl)
+	cq.DeleteFromLocalQueue(log, qImpl)
 	if cq.PendingTotal() != 0 {
 		t.Error("clusterQueue should be empty")
 	}
@@ -420,7 +420,7 @@ func TestClusterQueueImpl(t *testing.T) {
 
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
-			ctx, _ := utiltesting.ContextWithLog(t)
+			ctx, log := utiltesting.ContextWithLog(t)
 			cq := newClusterQueueImpl(ctx, nil, defaultOrdering, fakeClock, nil, false, nil)
 			err := cq.Update(utiltestingapi.MakeClusterQueue("cq").
 				NamespaceSelector(&metav1.LabelSelector{
@@ -452,7 +452,7 @@ func TestClusterQueueImpl(t *testing.T) {
 			}
 
 			for _, w := range test.workloadsToDelete {
-				cq.Delete(w)
+				cq.Delete(log, w)
 			}
 
 			if test.queueInadmissibleWorkloads {
@@ -645,7 +645,7 @@ func TestBestEffortFIFORequeueIfNotPresent(t *testing.T) {
 }
 
 func TestFIFOClusterQueue(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	q, err := newClusterQueue(ctx, nil,
 		&kueue.ClusterQueue{
 			Spec: kueue.ClusterQueueSpec{
@@ -704,7 +704,7 @@ func TestFIFOClusterQueue(t *testing.T) {
 		t.Errorf("Popped workload %q want %q", got.Obj.Name, "after")
 	}
 
-	q.Delete(&kueue.Workload{
+	q.Delete(log, &kueue.Workload{
 		ObjectMeta: metav1.ObjectMeta{Name: "now"},
 	})
 	got = q.Pop()
diff --git a/pkg/cache/queue/manager_test.go b/pkg/cache/queue/manager_test.go
index 7674d5d69..420e5edef 100644
--- a/pkg/cache/queue/manager_test.go
+++ b/pkg/cache/queue/manager_test.go
@@ -24,6 +24,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
@@ -307,7 +308,7 @@ func TestClusterQueueToActive(t *testing.T) {
 // TestUpdateLocalQueue tests that workloads are transferred between clusterQueues
 // when the queue points to a different clusterQueue.
 func TestUpdateLocalQueue(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	clusterQueues := []*kueue.ClusterQueue{
 		utiltestingapi.MakeClusterQueue("cq1").Obj(),
 		utiltestingapi.MakeClusterQueue("cq2").Obj(),
@@ -340,7 +341,7 @@ func TestUpdateLocalQueue(t *testing.T) {
 
 	// Update cluster queue of first queue.
 	queues[0].Spec.ClusterQueue = "cq2"
-	if err := manager.UpdateLocalQueue(queues[0]); err != nil {
+	if err := manager.UpdateLocalQueue(log, queues[0]); err != nil {
 		t.Fatalf("Failed updating queue: %v", err)
 	}
 
@@ -361,7 +362,7 @@ func TestUpdateLocalQueue(t *testing.T) {
 // TestDeleteLocalQueue tests that when a LocalQueue is deleted, all its
 // workloads are not listed in the ClusterQueue.
 func TestDeleteLocalQueue(t *testing.T) {
-	ctx, _ := utiltesting.ContextWithLog(t)
+	ctx, log := utiltesting.ContextWithLog(t)
 	cq := utiltestingapi.MakeClusterQueue("cq").Obj()
 	q := utiltestingapi.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj()
 	wl := utiltestingapi.MakeWorkload("a", "").Queue("foo").Obj()
@@ -383,7 +384,7 @@ func TestDeleteLocalQueue(t *testing.T) {
 		t.Errorf("Unexpected workloads after setup (-want,+got):\n%s", diff)
 	}
 
-	manager.DeleteLocalQueue(q)
+	manager.DeleteLocalQueue(log, q)
 	wantActiveWorkloads = nil
 	if diff := cmp.Diff(wantActiveWorkloads, manager.Dump(), cmpDump...); diff != "" {
 		t.Errorf("Unexpected workloads after deleting LocalQueue (-want,+got):\n%s", diff)
@@ -777,7 +778,7 @@ func TestUpdateWorkload(t *testing.T) {
 	}
 	for name, tc := range cases {
 		t.Run(name, func(t *testing.T) {
-			ctx, _ := utiltesting.ContextWithLog(t)
+			ctx, log := utiltesting.ContextWithLog(t)
 			manager := NewManager(utiltesting.NewFakeClient(), nil)
 			for _, cq := range tc.clusterQueues {
 				if err := manager.AddClusterQueue(ctx, cq); err != nil {
@@ -794,7 +795,7 @@ func TestUpdateWorkload(t *testing.T) {
 			}
 			wl := tc.workloads[0].DeepCopy()
 			tc.update(wl)
-			err := manager.UpdateWorkload(tc.workloads[0], wl)
+			err := manager.UpdateWorkload(log, tc.workloads[0], wl)
 			if diff := cmp.Diff(tc.wantErr, err, cmpopts.EquateErrors()); len(diff) != 0 {
 				t.Errorf("Unexpected UpdatedWorkload returned error (-want,+got):\n%s", diff)
 			}
@@ -1032,7 +1033,8 @@ func TestHeadsAsync(t *testing.T) {
 				go func() {
 					wlCopy := wl.DeepCopy()
 					wlCopy.ResourceVersion = "old"
-					if err := mgr.UpdateWorkload(wlCopy, &wl); err != nil {
+					log := logr.FromContextOrDiscard(ctx)
+					if err := mgr.UpdateWorkload(log, wlCopy, &wl); err != nil {
 						t.Errorf("Failed to add or update workload: %v", err)
 					}
 				}()
diff --git a/pkg/cache/scheduler/tas_cache_test.go b/pkg/cache/scheduler/tas_cache_test.go
index 586dcb972..a0bd663f9 100644
--- a/pkg/cache/scheduler/tas_cache_test.go
+++ b/pkg/cache/scheduler/tas_cache_test.go
@@ -30,6 +30,7 @@ import (
 	tasindexer "sigs.k8s.io/kueue/pkg/controller/tas/indexer"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/resources"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 	testingpod "sigs.k8s.io/kueue/pkg/util/testingjobs/pod"
@@ -44,7 +45,7 @@ type PodSetTestCase struct {
 	tolerations     []corev1.Toleration
 	nodeSelector    map[string]string
 	podSetGroupName *string
-	wantAssignment  *kueue.TopologyAssignment
+	wantAssignment  *tas.TopologyAssignment
 	wantReason      string
 }
 
@@ -463,9 +464,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -543,9 +544,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -610,9 +611,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -634,9 +635,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -656,9 +657,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{Count: 1, Values: []string{"x1"}},
 						{Count: 1, Values: []string{"x3"}},
 						{Count: 1, Values: []string{"x5"}},
@@ -679,9 +680,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{Count: 1, Values: []string{"x1"}},
 						{Count: 1, Values: []string{"x3"}},
 						{Count: 1, Values: []string{"x5"}},
@@ -702,9 +703,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -726,9 +727,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -751,9 +752,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -776,9 +777,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -818,9 +819,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -843,9 +844,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -867,9 +868,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -892,9 +893,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -917,9 +918,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -957,9 +958,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -982,9 +983,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1007,9 +1008,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1032,9 +1033,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -1058,9 +1059,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 3,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 3,
 							Values: []string{
@@ -1108,9 +1109,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 5,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{tasBlockLabel},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1138,9 +1139,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -1177,9 +1178,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1202,9 +1203,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 2,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1233,12 +1234,12 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{
 						tasBlockLabel,
 						tasRackLabel,
 					},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1261,12 +1262,12 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{
 						tasBlockLabel,
 						tasRackLabel,
 					},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1296,9 +1297,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1356,9 +1357,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceMemory: 1024,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 4,
 							Values: []string{
@@ -1381,9 +1382,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1413,9 +1414,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1452,9 +1453,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultTwoLevels,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1543,9 +1544,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1612,9 +1613,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 600,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1656,9 +1657,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 600,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1768,9 +1769,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 600,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -1902,9 +1903,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -2061,9 +2062,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 1,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -2127,9 +2128,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -2212,9 +2213,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 12,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 4,
 							Values: []string{
@@ -2287,9 +2288,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -2396,9 +2397,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -2440,9 +2441,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -2515,9 +2516,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -2584,9 +2585,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -2675,9 +2676,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 8,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -2762,9 +2763,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 12,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 3,
 							Values: []string{
@@ -2827,9 +2828,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 25,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  13,
 							Values: []string{"x1"},
@@ -2892,9 +2893,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 23,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  12,
 							Values: []string{"x1"},
@@ -2960,9 +2961,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 25,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  15,
 							Values: []string{"x2"},
@@ -3036,9 +3037,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 22,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  11,
 							Values: []string{"x3"},
@@ -3112,9 +3113,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 25,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  14,
 							Values: []string{"x1"},
@@ -3190,9 +3191,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 25,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  15,
 							Values: []string{"x3"},
@@ -3285,9 +3286,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 20,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{corev1.LabelHostname},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  10,
 							Values: []string{"x3"},
@@ -3383,9 +3384,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 20,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{corev1.LabelHostname},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  10,
 							Values: []string{"x3"},
@@ -3471,9 +3472,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 20,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: []string{tasBlockLabel, tasSubBlockLabel, tasRackLabel},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  10,
 							Values: []string{"b1", "sb2", "r3"},
@@ -3559,9 +3560,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 22,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  12,
 							Values: []string{"x2"},
@@ -3644,9 +3645,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 22,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  11,
 							Values: []string{"x3"},
@@ -3715,9 +3716,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultOneLevel,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  1,
 								Values: []string{"x3"},
@@ -3737,9 +3738,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           25,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultOneLevel,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  15,
 								Values: []string{"x2"},
@@ -3823,9 +3824,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 15,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  5,
 							Values: []string{"x3"},
@@ -3912,9 +3913,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 24,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  8,
 							Values: []string{"x1"},
@@ -3994,9 +3995,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 23,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  12,
 							Values: []string{"x1"},
@@ -4129,9 +4130,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					"example.com/gpu": 1,
 				},
 				count: 20,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count:  5,
 							Values: []string{"x1"},
@@ -4197,9 +4198,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultOneLevel,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  1,
 								Values: []string{"x1"},
@@ -4217,9 +4218,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           10,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultOneLevel,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  5,
 								Values: []string{"x1"},
@@ -4419,9 +4420,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 3,
 							Values: []string{
@@ -4534,9 +4535,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 2,
 							Values: []string{
@@ -4571,9 +4572,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 6,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 4,
 							Values: []string{
@@ -4602,9 +4603,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					corev1.ResourceCPU: 1000,
 				},
 				count: 4,
-				wantAssignment: &kueue.TopologyAssignment{
+				wantAssignment: &tas.TopologyAssignment{
 					Levels: defaultOneLevel,
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Count: 1,
 							Values: []string{
@@ -4665,9 +4666,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 3,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 2,
 								Values: []string{
@@ -4692,9 +4693,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 3,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -4755,9 +4756,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -4778,9 +4779,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           4,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 4,
 								Values: []string{
@@ -4833,9 +4834,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  1,
 								Values: []string{"b1"},
@@ -4853,9 +4854,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						"example.com/gpu":  2,
 					},
 					count: 4,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  4,
 								Values: []string{"b2"},
@@ -4950,9 +4951,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -4973,9 +4974,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           4,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 4,
 								Values: []string{
@@ -5124,9 +5125,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -5147,9 +5148,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -5277,9 +5278,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -5300,9 +5301,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 					},
 					podSetGroupName: ptr.To("sameGroup"),
 					count:           2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -5333,9 +5334,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  2,
 								Values: []string{"b1", "r1"},
@@ -5352,9 +5353,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceMemory: 1024,
 					},
 					count: 1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count:  1,
 								Values: []string{"b1", "r1"},
@@ -5379,9 +5380,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 9,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 9, Values: []string{"x2"}},
 						},
 					},
@@ -5395,9 +5396,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 2, Values: []string{"x3"}},
 						},
 					},
@@ -5419,9 +5420,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 1, Values: []string{"b1", "r1"}},
 						},
 					},
@@ -5436,9 +5437,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU:    1000,
 					},
 					count: 1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 1, Values: []string{"b1", "r1"}},
 						},
 					},
@@ -5460,9 +5461,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 10,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 10, Values: []string{"b1", "r1"}},
 						},
 					},
@@ -5476,9 +5477,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: defaultTwoLevels,
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 2, Values: []string{"b2", "r1"}},
 						},
 					},
@@ -5500,9 +5501,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 8,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 8, Values: []string{"x2"}},
 						},
 					},
@@ -5516,9 +5517,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceCPU: 1000,
 					},
 					count: 2,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 2, Values: []string{"x2"}},
 						},
 					},
@@ -5558,9 +5559,9 @@ func TestFindTopologyAssignments(t *testing.T) {
 						corev1.ResourceMemory: 1000,
 					},
 					count: 1,
-					wantAssignment: &kueue.TopologyAssignment{
+					wantAssignment: &tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{Count: 1, Values: []string{"x1"}},
 						},
 					},
diff --git a/pkg/cache/scheduler/tas_flavor_snapshot_test.go b/pkg/cache/scheduler/tas_flavor_snapshot_test.go
index 48f08817b..97eb87175 100644
--- a/pkg/cache/scheduler/tas_flavor_snapshot_test.go
+++ b/pkg/cache/scheduler/tas_flavor_snapshot_test.go
@@ -26,6 +26,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	"sigs.k8s.io/kueue/pkg/resources"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	"sigs.k8s.io/kueue/pkg/util/testingjobs/node"
 )
@@ -80,14 +81,14 @@ func TestMergeTopologyAssignments(t *testing.T) {
 	levels := []string{"level-1", "level-2"}
 
 	cases := map[string]struct {
-		a    *kueue.TopologyAssignment
-		b    *kueue.TopologyAssignment
-		want kueue.TopologyAssignment
+		a    *tas.TopologyAssignment
+		b    *tas.TopologyAssignment
+		want tas.TopologyAssignment
 	}{
 		"topologies with different domains, all a before b": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -98,9 +99,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"d", "e"},
 						Count:  1,
@@ -111,9 +112,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -134,9 +135,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 			},
 		},
 		"topologies with different domains, all b before a": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"d", "e"},
 						Count:  1,
@@ -147,9 +148,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -160,9 +161,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -183,9 +184,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 			},
 		},
 		"topologies with different domains, mixed order": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "c"},
 						Count:  1,
@@ -196,9 +197,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -209,9 +210,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -232,9 +233,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 			},
 		},
 		"topologies with different and the same domains, mixed order": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "c"},
 						Count:  1,
@@ -245,9 +246,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -258,9 +259,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -277,13 +278,13 @@ func TestMergeTopologyAssignments(t *testing.T) {
 			},
 		},
 		"topology a with empty domains": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels:  []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{},
+				Domains: []tas.TopologyDomainAssignment{},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -294,9 +295,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "b"},
 						Count:  1,
@@ -309,9 +310,9 @@ func TestMergeTopologyAssignments(t *testing.T) {
 			},
 		},
 		"topology b with empty domain": {
-			a: &kueue.TopologyAssignment{
+			a: &tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "c"},
 						Count:  1,
@@ -322,13 +323,13 @@ func TestMergeTopologyAssignments(t *testing.T) {
 					},
 				},
 			},
-			b: &kueue.TopologyAssignment{
+			b: &tas.TopologyAssignment{
 				Levels:  []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{},
+				Domains: []tas.TopologyDomainAssignment{},
 			},
-			want: kueue.TopologyAssignment{
+			want: tas.TopologyAssignment{
 				Levels: []string{"level-1", "level-2"},
-				Domains: []kueue.TopologyDomainAssignment{
+				Domains: []tas.TopologyDomainAssignment{
 					{
 						Values: []string{"a", "c"},
 						Count:  1,
diff --git a/pkg/config/validation_test.go b/pkg/config/validation_test.go
index bb7e94a71..50ae105eb 100644
--- a/pkg/config/validation_test.go
+++ b/pkg/config/validation_test.go
@@ -416,7 +416,6 @@ func TestValidate(t *testing.T) {
 			cfg: &configapi.Configuration{
 				Integrations: defaultIntegrations,
 				FairSharing: &configapi.FairSharing{
-					Enable:               true,
 					PreemptionStrategies: []configapi.PreemptionStrategy{configapi.LessThanOrEqualToFinalShare, "UNKNOWN", configapi.LessThanInitialShare, configapi.LessThanOrEqualToFinalShare},
 				},
 			},
@@ -431,7 +430,6 @@ func TestValidate(t *testing.T) {
 			cfg: &configapi.Configuration{
 				Integrations: defaultIntegrations,
 				FairSharing: &configapi.FairSharing{
-					Enable:               true,
 					PreemptionStrategies: []configapi.PreemptionStrategy{configapi.LessThanOrEqualToFinalShare, configapi.LessThanInitialShare},
 				},
 			},
diff --git a/pkg/controller/tas/topology_ungater_test.go b/pkg/controller/tas/topology_ungater_test.go
index 37532aec3..33ea8432f 100644
--- a/pkg/controller/tas/topology_ungater_test.go
+++ b/pkg/controller/tas/topology_ungater_test.go
@@ -40,6 +40,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/constants"
 	"sigs.k8s.io/kueue/pkg/controller/tas/indexer"
 	utilpod "sigs.k8s.io/kueue/pkg/util/pod"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingpod "sigs.k8s.io/kueue/pkg/util/testingjobs/pod"
@@ -1715,9 +1716,9 @@ func TestReconcile(t *testing.T) {
 							PodSets(utiltestingapi.MakePodSetAssignment(kueue.DefaultPodSetName).
 								Assignment(corev1.ResourceCPU, "unit-test-flavor", "4").
 								Count(4).
-								TopologyAssignment(&kueue.TopologyAssignment{
+								TopologyAssignment(tas.V1Beta2From(&tas.TopologyAssignment{
 									Levels: defaultTestLevels,
-									Domains: []kueue.TopologyDomainAssignment{
+									Domains: []tas.TopologyDomainAssignment{
 										{
 											Count: 2,
 											Values: []string{
@@ -1740,7 +1741,7 @@ func TestReconcile(t *testing.T) {
 											},
 										},
 									},
-								}).
+								})).
 								Obj()).
 							Obj(),
 					).
diff --git a/pkg/podset/podset_test.go b/pkg/podset/podset_test.go
index db7e38992..e1bfb27e6 100644
--- a/pkg/podset/podset_test.go
+++ b/pkg/podset/podset_test.go
@@ -28,6 +28,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 )
@@ -174,15 +175,15 @@ func TestFromAssignment(t *testing.T) {
 				Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
 					corev1.ResourceCPU: kueue.ResourceFlavorReference(flavor1.Name),
 				},
-				TopologyAssignment: &kueue.TopologyAssignment{
+				TopologyAssignment: tas.V1Beta2From(&tas.TopologyAssignment{
 					Levels: []string{"cloud.com/rack"},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Values: []string{"rack1"},
 							Count:  4,
 						},
 					},
-				},
+				}),
 			},
 			defaultCount: 4,
 			flavors:      []kueue.ResourceFlavor{*flavor1.DeepCopy()},
@@ -210,15 +211,15 @@ func TestFromAssignment(t *testing.T) {
 				Flavors: map[corev1.ResourceName]kueue.ResourceFlavorReference{
 					corev1.ResourceCPU: kueue.ResourceFlavorReference(flavor1.Name),
 				},
-				TopologyAssignment: &kueue.TopologyAssignment{
+				TopologyAssignment: tas.V1Beta2From(&tas.TopologyAssignment{
 					Levels: []string{"cloud.com/rack"},
-					Domains: []kueue.TopologyDomainAssignment{
+					Domains: []tas.TopologyDomainAssignment{
 						{
 							Values: []string{"rack1"},
 							Count:  4,
 						},
 					},
-				},
+				}),
 			},
 			defaultCount: 4,
 			flavors:      []kueue.ResourceFlavor{*flavor1.DeepCopy()},
diff --git a/pkg/scheduler/preemption/preemption_fair_test.go b/pkg/scheduler/preemption/preemption_fair_test.go
index 38ef43f22..a84beec63 100644
--- a/pkg/scheduler/preemption/preemption_fair_test.go
+++ b/pkg/scheduler/preemption/preemption_fair_test.go
@@ -917,8 +917,7 @@ func TestFairPreemptions(t *testing.T) {
 			broadcaster := record.NewBroadcaster()
 			scheme := runtime.NewScheme()
 			recorder := broadcaster.NewRecorder(scheme, corev1.EventSource{Component: constants.AdmissionName})
-			preemptor := New(cl, workload.Ordering{}, recorder, config.FairSharing{
-				Enable:               true,
+			preemptor := New(cl, workload.Ordering{}, recorder, &config.FairSharing{
 				PreemptionStrategies: tc.strategies,
 			}, false, clocktesting.NewFakeClock(now))
 
diff --git a/pkg/scheduler/preemption/preemption_hierarchical_test.go b/pkg/scheduler/preemption/preemption_hierarchical_test.go
index 412920d88..827e0f7ef 100644
--- a/pkg/scheduler/preemption/preemption_hierarchical_test.go
+++ b/pkg/scheduler/preemption/preemption_hierarchical_test.go
@@ -30,7 +30,6 @@ import (
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta2"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
 	"sigs.k8s.io/kueue/pkg/constants"
@@ -1843,7 +1842,7 @@ func TestHierarchicalPreemptions(t *testing.T) {
 				t.Fatalf("Failed adding kueue scheme: %v", err)
 			}
 			recorder := broadcaster.NewRecorder(scheme, corev1.EventSource{Component: constants.AdmissionName})
-			preemptor := New(cl, workload.Ordering{}, recorder, config.FairSharing{}, false, clocktesting.NewFakeClock(now))
+			preemptor := New(cl, workload.Ordering{}, recorder, nil, false, clocktesting.NewFakeClock(now))
 
 			beforeSnapshot, err := cqCache.Snapshot(ctx)
 			if err != nil {
diff --git a/pkg/scheduler/preemption/preemption_test.go b/pkg/scheduler/preemption/preemption_test.go
index 65bdcc025..a3ebc87ce 100644
--- a/pkg/scheduler/preemption/preemption_test.go
+++ b/pkg/scheduler/preemption/preemption_test.go
@@ -32,7 +32,6 @@ import (
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta2"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	"sigs.k8s.io/kueue/pkg/cache/hierarchy"
 	schdcache "sigs.k8s.io/kueue/pkg/cache/scheduler"
@@ -4131,7 +4130,7 @@ func TestPreemption(t *testing.T) {
 				t.Fatalf("Failed adding kueue scheme: %v", err)
 			}
 			recorder := broadcaster.NewRecorder(scheme, corev1.EventSource{Component: constants.AdmissionName})
-			preemptor := New(cl, workload.Ordering{}, recorder, config.FairSharing{}, false, clocktesting.NewFakeClock(now))
+			preemptor := New(cl, workload.Ordering{}, recorder, nil, false, clocktesting.NewFakeClock(now))
 
 			beforeSnapshot, err := cqCache.Snapshot(ctx)
 			if err != nil {
diff --git a/pkg/scheduler/scheduler_afs_test.go b/pkg/scheduler/scheduler_afs_test.go
index 8ccbf77ed..3185cd79e 100644
--- a/pkg/scheduler/scheduler_afs_test.go
+++ b/pkg/scheduler/scheduler_afs_test.go
@@ -538,10 +538,7 @@ func TestScheduleForAFS(t *testing.T) {
 					WithInterceptorFuncs(interceptor.Funcs{SubResourcePatch: utiltesting.TreatSSAAsStrategicMerge})
 				cl := clientBuilder.Build()
 
-				fairSharing := &config.FairSharing{
-					Enable: tc.enableFairSharing,
-				}
-				cqCache := schdcache.New(cl, schdcache.WithFairSharing(fairSharing.Enable), schdcache.WithAdmissionFairSharing(afsConfig))
+				cqCache := schdcache.New(cl, schdcache.WithFairSharing(tc.enableFairSharing), schdcache.WithAdmissionFairSharing(afsConfig))
 				qManager := qcache.NewManager(cl, cqCache, qcache.WithAdmissionFairSharing(afsConfig))
 
 				ctx, log := utiltesting.ContextWithLog(t)
@@ -562,8 +559,12 @@ func TestScheduleForAFS(t *testing.T) {
 					}
 				}
 				recorder := &utiltesting.EventRecorder{}
+				var preemptionFairSharing *config.FairSharing
+				if tc.enableFairSharing {
+					preemptionFairSharing = &config.FairSharing{}
+				}
 				scheduler := New(qManager, cqCache, cl, recorder,
-					WithFairSharing(fairSharing),
+					WithFairSharing(preemptionFairSharing),
 					WithAdmissionFairSharing(afsConfig),
 					WithClock(t, fakeClock))
 				wg := sync.WaitGroup{}
diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go
index 5f579d2e2..99a29927f 100644
--- a/pkg/scheduler/scheduler_test.go
+++ b/pkg/scheduler/scheduler_test.go
@@ -7454,7 +7454,12 @@ func TestSchedule(t *testing.T) {
 					}
 				}
 
-				scheduler := New(qManager, cqCache, cl, recorder, WithFairSharing(&config.FairSharing{Enable: tc.enableFairSharing}), WithClock(t, fakeClock))
+				var fairSharing *config.FairSharing
+				if tc.enableFairSharing {
+					fairSharing = &config.FairSharing{}
+				}
+				scheduler := New(qManager, cqCache, cl, recorder,
+					WithFairSharing(fairSharing), WithClock(t, fakeClock))
 				wg := sync.WaitGroup{}
 				scheduler.setAdmissionRoutineWrapper(routine.NewWrapper(
 					func() { wg.Add(1) },
diff --git a/pkg/util/cmp/cmp_test.go b/pkg/util/cmp/cmp_test.go
new file mode 100644
index 000000000..e55c31446
--- /dev/null
+++ b/pkg/util/cmp/cmp_test.go
@@ -0,0 +1,59 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmp
+
+import (
+	"testing"
+)
+
+func TestCompareBool(t *testing.T) {
+	cases := map[string]struct {
+		a   bool
+		b   bool
+		exp int
+	}{
+		"both true": {
+			a:   true,
+			b:   true,
+			exp: 0,
+		},
+		"both false": {
+			a:   false,
+			b:   false,
+			exp: 0,
+		},
+		"a true, b false": {
+			a:   true,
+			b:   false,
+			exp: -1,
+		},
+		"a false, b true": {
+			a:   false,
+			b:   true,
+			exp: 1,
+		},
+	}
+
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			result := CompareBool(tc.a, tc.b)
+			if result != tc.exp {
+				t.Errorf("CompareBool(%t, %t) = %d; want %d", tc.a, tc.b, result, tc.exp)
+			}
+		})
+	}
+}
diff --git a/pkg/util/tas/tas_assignment_test.go b/pkg/util/tas/tas_assignment_test.go
new file mode 100644
index 000000000..299abc52e
--- /dev/null
+++ b/pkg/util/tas/tas_assignment_test.go
@@ -0,0 +1,791 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tas
+
+import (
+	"slices"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"k8s.io/utils/ptr"
+
+	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
+)
+
+type testCase struct {
+	name             string
+	internal         *TopologyAssignment
+	v1beta2          *kueue.TopologyAssignment
+	podCounts        []int32
+	totalDomainCount int
+}
+
+// bothWaysTestCases expect that internal <-> v1beta2 maps in both ways.
+var bothWaysTestCases = []testCase{
+	{
+		name: "empty",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{},
+		},
+		podCounts:        []int32{},
+		totalDomainCount: 0,
+	},
+	{
+		name: "one domain",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a1", "b1"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 1,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Universal: ptr.To("a1"),
+						},
+						{
+							Universal: ptr.To("b1"),
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1},
+		totalDomainCount: 1,
+	},
+	{
+		name: "multiple domains, same counts, no common prefix",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a1", "b1"},
+					Count:  1,
+				},
+				{
+					Values: []string{"a2", "b2"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("a"),
+								Roots:        []string{"1", "2"},
+							},
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("b"),
+								Roots:        []string{"1", "2"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1},
+		totalDomainCount: 2,
+	},
+	{
+		name: "multiple domains, different counts, common prefix and suffix",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"p.a1.s", "p.b1.s"},
+					Count:  1,
+				},
+				{
+					Values: []string{"p.a2.s", "p.b2.s"},
+					Count:  2,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Individual: []int32{1, 2},
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("p.a"),
+								CommonSuffix: ptr.To(".s"),
+								Roots:        []string{"1", "2"},
+							},
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("p.b"),
+								CommonSuffix: ptr.To(".s"),
+								Roots:        []string{"1", "2"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 2},
+		totalDomainCount: 2,
+	},
+	{
+		name: "multiple domains, universal value",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a1", "x"},
+					Count:  1,
+				},
+				{
+					Values: []string{"a1", "y"},
+					Count:  2,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Individual: []int32{1, 2},
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Universal: ptr.To("a1"),
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								Roots: []string{"x", "y"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 2},
+		totalDomainCount: 2,
+	},
+	{
+		name: "multiple domains, same counts, shrinking prefix",
+		internal: &TopologyAssignment{
+			Levels: []string{"a"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a123"},
+					Count:  1,
+				},
+				{
+					Values: []string{"a12"},
+					Count:  1,
+				},
+				{
+					Values: []string{"a1"},
+					Count:  1,
+				},
+				{
+					Values: []string{"a13"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 4,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("a1"),
+								Roots:        []string{"23", "2", "", "3"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1, 1, 1},
+		totalDomainCount: 4,
+	},
+	{
+		name: "multiple domains, same counts, shrinking suffix",
+		internal: &TopologyAssignment{
+			Levels: []string{"a"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"123b"},
+					Count:  1,
+				},
+				{
+					Values: []string{"23b"},
+					Count:  1,
+				},
+				{
+					Values: []string{"3b"},
+					Count:  1,
+				},
+				{
+					Values: []string{"13b"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 4,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonSuffix: ptr.To("3b"),
+								Roots:        []string{"12", "2", "", "1"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1, 1, 1},
+		totalDomainCount: 4,
+	},
+	{
+		name: "multiple domains, same counts, max prefix & suffix overlap partially",
+		internal: &TopologyAssignment{
+			Levels: []string{"a"},
+			Domains: []TopologyDomainAssignment{
+				// Max prefix: "abaca"
+				// Max suffix: "acada"
+				// They overlap - we must shrink them so that they don't.
+				// (The current implementation chooses to shrink the prefix: "abaca" -> "ab").
+				{
+					Values: []string{"abacacada"},
+					Count:  1,
+				},
+				{
+					Values: []string{"abacada"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("ab"),
+								CommonSuffix: ptr.To("acada"),
+								Roots:        []string{"ac", ""},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1},
+		totalDomainCount: 2,
+	},
+	{
+		name: "multiple domains, same counts, max prefix & suffix overlap fully, shrinking strings",
+		internal: &TopologyAssignment{
+			Levels: []string{"a"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"ababa"},
+					Count:  1,
+				},
+				{
+					Values: []string{"aba"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						// Prefix shrunk to "" -> it shouldn't be set at all.
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonSuffix: ptr.To("aba"),
+								Roots:        []string{"ab", ""},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1},
+		totalDomainCount: 2,
+	},
+	{
+		name: "multiple domains, same counts, max prefix & suffix overlap fully, growing strings",
+		internal: &TopologyAssignment{
+			Levels: []string{"a"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"aba"},
+					Count:  1,
+				},
+				{
+					Values: []string{"ababa"},
+					Count:  1,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(1)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						// Prefix shrunk to "" -> it shouldn't be set at all.
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonSuffix: ptr.To("aba"),
+								Roots:        []string{"", "ab"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 1},
+		totalDomainCount: 2,
+	},
+}
+
+// oneWayTestCases expect that v1beta2 -> internal (via InternalFrom).
+// (these v1beta2 values cannot be produced by V1Beta2From as of now - though this may change in the future)
+var oneWayTestCases = []testCase{
+	{
+		name: "multiple slices, no prefixes/suffixes or universal values",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a", "b"},
+					Count:  1,
+				},
+				{
+					Values: []string{"c", "d"},
+					Count:  2,
+				},
+				{
+					Values: []string{"e", "f"},
+					Count:  3,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 1,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Individual: []int32{1},
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								Roots: []string{"a"},
+							},
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								Roots: []string{"b"},
+							},
+						},
+					},
+				},
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Individual: []int32{2, 3},
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								Roots: []string{"c", "e"},
+							},
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								Roots: []string{"d", "f"},
+							},
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{1, 2, 3},
+		totalDomainCount: 3,
+	},
+	{
+		name: "multiple slices, prefixes, suffixes & universal values",
+		internal: &TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Domains: []TopologyDomainAssignment{
+				{
+					Values: []string{"a1", "b1-s"},
+					Count:  2,
+				},
+				{
+					Values: []string{"a1", "b2-s"},
+					Count:  3,
+				},
+				{
+					Values: []string{"a2", "x-t"},
+					Count:  5,
+				},
+				{
+					Values: []string{"a3", "y-t"},
+					Count:  5,
+				},
+				{
+					Values: []string{"a4", "z-t"},
+					Count:  5,
+				},
+				{
+					Values: []string{"a10", "b10"},
+					Count:  10,
+				},
+				{
+					Values: []string{"a10", "b10"},
+					Count:  10,
+				},
+			},
+		},
+		v1beta2: &kueue.TopologyAssignment{
+			Levels: []string{"a", "b"},
+			Slices: []kueue.TopologyAssignmentSlice{
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Individual: []int32{2, 3},
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Universal: ptr.To("a1"),
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("b"),
+								CommonSuffix: ptr.To("-s"),
+								Roots:        []string{"1", "2"},
+							},
+						},
+					},
+				},
+				{
+					DomainCount: 3,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(5)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonPrefix: ptr.To("a"),
+								Roots:        []string{"2", "3", "4"},
+							},
+						},
+						{
+							Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+								CommonSuffix: ptr.To("-t"),
+								Roots:        []string{"x", "y", "z"},
+							},
+						},
+					},
+				},
+				{
+					DomainCount: 2,
+					PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+						Universal: ptr.To(int32(10)),
+					},
+					ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+						{
+							Universal: ptr.To("a10"),
+						},
+						{
+							Universal: ptr.To("b10"),
+						},
+					},
+				},
+			},
+		},
+		podCounts:        []int32{2, 3, 5, 5, 5, 10, 10},
+		totalDomainCount: 7,
+	},
+}
+
+var twoDomains = &kueue.TopologyAssignment{
+	Levels: []string{"a"},
+	Slices: []kueue.TopologyAssignmentSlice{
+		{
+			DomainCount: 2,
+			PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+				Universal: ptr.To(int32(1)),
+			},
+			ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+				{
+					Universal: ptr.To("a1"),
+				},
+			},
+		},
+	},
+}
+
+func TestV1Beta2FromInternal(t *testing.T) {
+	for _, tc := range bothWaysTestCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := V1Beta2From(tc.internal)
+			if diff := cmp.Diff(tc.v1beta2, got, cmpopts.EquateEmpty()); diff != "" {
+				t.Errorf("unexpected result (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestV1Beta2FromInternal_forNil(t *testing.T) {
+	got := V1Beta2From(nil)
+	if got != nil {
+		t.Errorf("unexpected result for nil: %+v", got)
+	}
+}
+
+func TestInternalFromV1Beta2(t *testing.T) {
+	for _, tc := range slices.Concat(bothWaysTestCases, oneWayTestCases) {
+		t.Run(tc.name, func(t *testing.T) {
+			got := InternalFrom(tc.v1beta2)
+			if diff := cmp.Diff(tc.internal, got, cmpopts.EquateEmpty()); diff != "" {
+				t.Errorf("unexpected result (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestInternalFromV1Beta2_forNil(t *testing.T) {
+	got := InternalFrom(nil)
+	if got != nil {
+		t.Errorf("unexpected result for nil: %+v", got)
+	}
+}
+
+func TestInternalSeqFromV1Beta2(t *testing.T) {
+	for _, tc := range slices.Concat(bothWaysTestCases, oneWayTestCases) {
+		t.Run(tc.name, func(t *testing.T) {
+			seq := InternalSeqFrom(tc.v1beta2)
+			got := slices.Collect(seq)
+			want := tc.internal.Domains
+			if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
+				t.Errorf("unexpected result (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestInternalSeqFromV1Beta2_forNil(t *testing.T) {
+	got := InternalSeqFrom(nil)
+	if got != nil {
+		t.Errorf("unexpected result for nil: %+v", got)
+	}
+}
+
+func TestInternalSeqFromV1Beta2_iteratorStops(t *testing.T) {
+	for range InternalSeqFrom(twoDomains) {
+		// Break the loop prematurely.
+		// If the iterator isn't smart enough to stop, this will panic.
+		break
+	}
+}
+
+func TestPodCounts(t *testing.T) {
+	for _, tc := range slices.Concat(bothWaysTestCases, oneWayTestCases) {
+		t.Run(tc.name, func(t *testing.T) {
+			seq := PodCounts(tc.v1beta2)
+			got := slices.Collect(seq)
+			want := tc.podCounts
+			if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
+				t.Errorf("unexpected result (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestPodCounts_forNil(t *testing.T) {
+	got := PodCounts(nil)
+	if got != nil {
+		t.Errorf("unexpected result for nil: %+v", got)
+	}
+}
+
+func TestPodCounts_iteratorStops(t *testing.T) {
+	for range PodCounts(twoDomains) {
+		// Break the loop prematurely.
+		// If the iterator isn't smart enough to stop, this will panic.
+		break
+	}
+}
+
+func TestTotalDomainCount(t *testing.T) {
+	for _, tc := range slices.Concat(bothWaysTestCases, oneWayTestCases) {
+		t.Run(tc.name, func(t *testing.T) {
+			got := TotalDomainCount(tc.v1beta2)
+			if diff := cmp.Diff(tc.totalDomainCount, got); diff != "" {
+				t.Errorf("unexpected result (-want,+got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestTotalDomainCount_forNil(t *testing.T) {
+	got := TotalDomainCount(nil)
+	if got != 0 {
+		t.Errorf("unexpected result for nil: %d", got)
+	}
+}
+
+func TestLowestLevel(t *testing.T) {
+	v1beta2 := &kueue.TopologyAssignment{
+		Levels: []string{"a", "b"},
+		Slices: []kueue.TopologyAssignmentSlice{
+			{
+				DomainCount: 2,
+				PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+					Individual: []int32{2, 3},
+				},
+				ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+					{
+						Universal: ptr.To("a1"),
+					},
+					{
+						Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+							CommonPrefix: ptr.To("b"),
+							CommonSuffix: ptr.To("-s"),
+							Roots:        []string{"1", "2"},
+						},
+					},
+				},
+			},
+			{
+				DomainCount: 3,
+				PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+					Universal: ptr.To(int32(5)),
+				},
+				ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+					{
+						Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+							CommonPrefix: ptr.To("a"),
+							Roots:        []string{"2", "3", "4"},
+						},
+					},
+					{
+						Individual: &kueue.TopologyAssignmentSliceLevelIndividualValues{
+							CommonSuffix: ptr.To("-t"),
+							Roots:        []string{"x", "y", "z"},
+						},
+					},
+				},
+			},
+			{
+				DomainCount: 2,
+				PodCounts: kueue.TopologyAssignmentSlicePodCounts{
+					Universal: ptr.To(int32(10)),
+				},
+				ValuesPerLevel: []kueue.TopologyAssignmentSliceLevelValues{
+					{
+						Universal: ptr.To("a10"),
+					},
+					{
+						Universal: ptr.To("b10"),
+					},
+				},
+			},
+		},
+	}
+	want := []string{"b1-s", "b2-s", "x-t", "y-t", "z-t", "b10", "b10"}
+	seq := LowestLevelValues(v1beta2)
+	got := slices.Collect(seq)
+	if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
+		t.Errorf("unexpected result (-want,+got):\n%s", diff)
+	}
+}
+
+func TestLowestLevelValues_forNil(t *testing.T) {
+	got := LowestLevelValues(nil)
+	if got != nil {
+		t.Errorf("unexpected result for nil: %+v", got)
+	}
+}
+
+func TestLowestLevelValues_iteratorStops(t *testing.T) {
+	for range LowestLevelValues(twoDomains) {
+		// Break the loop prematurely.
+		// If the iterator isn't smart enough to stop, this will panic.
+		break
+	}
+}
diff --git a/pkg/util/testing/v1beta2/wrappers.go b/pkg/util/testing/v1beta2/wrappers.go
index 6c2198efd..53e1c3a56 100644
--- a/pkg/util/testing/v1beta2/wrappers.go
+++ b/pkg/util/testing/v1beta2/wrappers.go
@@ -32,6 +32,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	"sigs.k8s.io/kueue/pkg/controller/constants"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 )
 
@@ -1196,29 +1197,29 @@ func (t *TopologyWrapper) Obj() *kueue.Topology {
 }
 
 type TopologyDomainAssignmentWrapper struct {
-	kueue.TopologyDomainAssignment
+	tas.TopologyDomainAssignment
 }
 
 func MakeTopologyDomainAssignment(values []string, count int32) *TopologyDomainAssignmentWrapper {
 	return &TopologyDomainAssignmentWrapper{
-		TopologyDomainAssignment: kueue.TopologyDomainAssignment{
+		TopologyDomainAssignment: tas.TopologyDomainAssignment{
 			Values: values,
 			Count:  count,
 		},
 	}
 }
 
-func (t *TopologyDomainAssignmentWrapper) Obj() kueue.TopologyDomainAssignment {
+func (t *TopologyDomainAssignmentWrapper) Obj() tas.TopologyDomainAssignment {
 	return t.TopologyDomainAssignment
 }
 
 type TopologyAssignmentWrapper struct {
-	kueue.TopologyAssignment
+	tas.TopologyAssignment
 }
 
 func MakeTopologyAssignment(levels []string) *TopologyAssignmentWrapper {
 	return &TopologyAssignmentWrapper{
-		TopologyAssignment: kueue.TopologyAssignment{
+		TopologyAssignment: tas.TopologyAssignment{
 			Levels: levels,
 		},
 	}
@@ -1229,18 +1230,18 @@ func (t *TopologyAssignmentWrapper) Levels(levels ...string) *TopologyAssignment
 	return t
 }
 
-func (t *TopologyAssignmentWrapper) Domains(domains ...kueue.TopologyDomainAssignment) *TopologyAssignmentWrapper {
+func (t *TopologyAssignmentWrapper) Domains(domains ...tas.TopologyDomainAssignment) *TopologyAssignmentWrapper {
 	t.TopologyAssignment.Domains = domains
 	return t
 }
 
-func (t *TopologyAssignmentWrapper) Domain(domain kueue.TopologyDomainAssignment) *TopologyAssignmentWrapper {
+func (t *TopologyAssignmentWrapper) Domain(domain tas.TopologyDomainAssignment) *TopologyAssignmentWrapper {
 	t.TopologyAssignment.Domains = append(t.TopologyAssignment.Domains, domain)
 	return t
 }
 
 func (t *TopologyAssignmentWrapper) Obj() *kueue.TopologyAssignment {
-	return &t.TopologyAssignment
+	return tas.V1Beta2From(&t.TopologyAssignment)
 }
 
 type PodSetAssignmentWrapper struct {
diff --git a/pkg/webhooks/workload_webhook_test.go b/pkg/webhooks/workload_webhook_test.go
index dab56c982..af3f3f615 100644
--- a/pkg/webhooks/workload_webhook_test.go
+++ b/pkg/webhooks/workload_webhook_test.go
@@ -29,6 +29,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	"sigs.k8s.io/kueue/pkg/workload"
@@ -390,15 +391,15 @@ func TestValidateWorkloadUpdate(t *testing.T) {
 					utiltestingapi.MakeAdmission("cluster-queue").
 						PodSets(kueue.PodSetAssignment{
 							Name: "ps1",
-							TopologyAssignment: &kueue.TopologyAssignment{
+							TopologyAssignment: tas.V1Beta2From(&tas.TopologyAssignment{
 								Levels: []string{"level"},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []tas.TopologyDomainAssignment{
 									{
 										Values: []string{"abc"},
 										Count:  2,
 									},
 								},
-							},
+							}),
 						}).
 						Obj(),
 				).
@@ -411,15 +412,15 @@ func TestValidateWorkloadUpdate(t *testing.T) {
 					utiltestingapi.MakeAdmission("cluster-queue").
 						PodSets(kueue.PodSetAssignment{
 							Name: "ps1",
-							TopologyAssignment: &kueue.TopologyAssignment{
+							TopologyAssignment: tas.V1Beta2From(&tas.TopologyAssignment{
 								Levels: []string{"level"},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []tas.TopologyDomainAssignment{
 									{
 										Values: []string{"abc"},
 										Count:  3,
 									},
 								},
-							},
+							}),
 						}).
 						Obj(),
 				).
diff --git a/test/e2e/config/certmanager/controller_manager_config.yaml b/test/e2e/config/certmanager/controller_manager_config.yaml
index 7bbcc75c5..2a667dc6e 100644
--- a/test/e2e/config/certmanager/controller_manager_config.yaml
+++ b/test/e2e/config/certmanager/controller_manager_config.yaml
@@ -12,8 +12,7 @@ controller:
     LocalQueue.kueue.x-k8s.io: 1
     ClusterQueue.kueue.x-k8s.io: 1
     ResourceFlavor.kueue.x-k8s.io: 1
-fairSharing:
-  enable: true
+fairSharing: {}
 internalCertManagement:
   enable: false
 clientConnection:
diff --git a/test/e2e/config/certmanager/values.yaml b/test/e2e/config/certmanager/values.yaml
index da13f7b91..937ddeb37 100644
--- a/test/e2e/config/certmanager/values.yaml
+++ b/test/e2e/config/certmanager/values.yaml
@@ -44,8 +44,7 @@ managerConfig:
         LocalQueue.kueue.x-k8s.io: 1
         ClusterQueue.kueue.x-k8s.io: 1
         ResourceFlavor.kueue.x-k8s.io: 1
-    fairSharing:
-      enable: true
+    fairSharing: {}
     internalCertManagement:
       enable: false
     clientConnection:
diff --git a/test/e2e/multikueue/tas_test.go b/test/e2e/multikueue/tas_test.go
index 938e528d7..87851ab07 100644
--- a/test/e2e/multikueue/tas_test.go
+++ b/test/e2e/multikueue/tas_test.go
@@ -34,6 +34,7 @@ import (
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	"sigs.k8s.io/kueue/pkg/util/admissioncheck"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -259,7 +260,7 @@ var _ = ginkgo.Describe("MultiKueue with TopologyAwareScheduling", func() {
 					g.Expect(workerWl.Status.Admission.PodSetAssignments).To(gomega.HaveLen(1))
 					g.Expect(workerWl.Status.Admission.PodSetAssignments[0].TopologyAssignment).NotTo(gomega.BeNil())
 					g.Expect(workerWl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Levels).To(gomega.Equal([]string{corev1.LabelHostname}))
-					g.Expect(workerWl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).NotTo(gomega.BeEmpty())
+					g.Expect(tas.TotalDomainCount(workerWl.Status.Admission.PodSetAssignments[0].TopologyAssignment)).NotTo(gomega.BeZero())
 				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
 			})
 
diff --git a/test/e2e/singlecluster/tas_test.go b/test/e2e/singlecluster/tas_test.go
index e04cc768e..3e0ff1e35 100644
--- a/test/e2e/singlecluster/tas_test.go
+++ b/test/e2e/singlecluster/tas_test.go
@@ -34,6 +34,7 @@ import (
 	podcontroller "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	podconstants "sigs.k8s.io/kueue/pkg/controller/jobs/pod/constants"
 	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -119,11 +120,11 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{
 							corev1.LabelHostname,
 						},
-						Domains: []kueue.TopologyDomainAssignment{
+						Domains: []tas.TopologyDomainAssignment{
 							{
 								Count: 1,
 								Values: []string{
@@ -131,7 +132,7 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 								},
 							},
 						},
-					},
+					}),
 				))
 			})
 
@@ -251,22 +252,22 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 				gomega.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{{
+						Domains: []tas.TopologyDomainAssignment{{
 							Count:  1,
 							Values: []string{"kind-worker"},
 						}},
-					},
+					}),
 				))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{{
+						Domains: []tas.TopologyDomainAssignment{{
 							Count:  1,
 							Values: []string{"kind-worker"},
 						}},
-					},
+					}),
 				))
 			})
 
@@ -361,13 +362,13 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 				gomega.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{{
+						Domains: []tas.TopologyDomainAssignment{{
 							Count:  1,
 							Values: []string{"kind-worker"},
 						}},
-					},
+					}),
 				))
 			})
 
@@ -425,13 +426,13 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 				gomega.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{{
+						Domains: []tas.TopologyDomainAssignment{{
 							Count:  2,
 							Values: []string{"kind-worker"},
 						}},
-					},
+					}),
 				))
 			})
 
@@ -539,13 +540,13 @@ var _ = ginkgo.Describe("TopologyAwareScheduling", func() {
 				gomega.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels: []string{corev1.LabelHostname},
-						Domains: []kueue.TopologyDomainAssignment{{
+						Domains: []tas.TopologyDomainAssignment{{
 							Count:  1,
 							Values: []string{"kind-worker"},
 						}},
-					},
+					}),
 				))
 			})
 
diff --git a/test/e2e/tas/hotswap_test.go b/test/e2e/tas/hotswap_test.go
index a8d56c728..b412ecf1a 100644
--- a/test/e2e/tas/hotswap_test.go
+++ b/test/e2e/tas/hotswap_test.go
@@ -31,6 +31,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -292,9 +293,9 @@ func expectWorkloadTopologyAssignment(ctx context.Context, k8sClient client.Clie
 		topologyAssignment := wl.Status.Admission.PodSetAssignments[0].TopologyAssignment
 		g.Expect(topologyAssignment).NotTo(gomega.BeNil())
 		g.Expect(topologyAssignment.Levels).To(gomega.BeEquivalentTo([]string{corev1.LabelHostname}))
-		g.Expect(topologyAssignment.Domains).To(gomega.HaveLen(numPods))
+		g.Expect(tas.TotalDomainCount(topologyAssignment)).To(gomega.Equal(numPods))
 		chosenNodes := []string{}
-		for _, domain := range topologyAssignment.Domains {
+		for domain := range tas.InternalSeqFrom(topologyAssignment) {
 			g.Expect(domain.Count).To(gomega.Equal(int32(1)))
 			chosenNodes = append(chosenNodes, domain.Values...)
 		}
diff --git a/test/e2e/tas/job_test.go b/test/e2e/tas/job_test.go
index 27d01b210..d89e1a3ad 100644
--- a/test/e2e/tas/job_test.go
+++ b/test/e2e/tas/job_test.go
@@ -30,6 +30,7 @@ import (
 
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta2"
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -133,27 +134,24 @@ var _ = ginkgo.Describe("TopologyAwareScheduling for Job", func() {
 					g.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
-				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment.Levels).Should(gomega.BeComparableTo(
-					[]string{
-						corev1.LabelHostname,
-					},
-				))
-				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).Should(gomega.BeComparableTo(
-					[]kueue.TopologyDomainAssignment{
-						{
-							Values: []string{"kind-worker"},
-							Count:  1,
+				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
+					tas.V1Beta2From(&tas.TopologyAssignment{
+						Levels: []string{corev1.LabelHostname},
+						Domains: []tas.TopologyDomainAssignment{
+							{
+								Values: []string{"kind-worker"},
+								Count:  1,
+							},
+							{
+								Values: []string{"kind-worker2"},
+								Count:  1,
+							},
+							{
+								Values: []string{"kind-worker3"},
+								Count:  1,
+							},
 						},
-						{
-							Values: []string{"kind-worker2"},
-							Count:  1,
-						},
-						{
-							Values: []string{"kind-worker3"},
-							Count:  1,
-						},
-					},
-				))
+					})))
 			})
 			ginkgo.By(fmt.Sprintf("verify the workload %q gets finished", wlLookupKey), func() {
 				gomega.Eventually(func(g gomega.Gomega) {
@@ -183,27 +181,23 @@ var _ = ginkgo.Describe("TopologyAwareScheduling for Job", func() {
 					g.Expect(createdWorkload.Status.Admission).ShouldNot(gomega.BeNil())
 				}, util.LongTimeout, util.Interval).Should(gomega.Succeed())
 				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
-				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment.Levels).Should(gomega.BeComparableTo(
-					[]string{
-						corev1.LabelHostname,
-					},
-				))
-				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).Should(gomega.BeComparableTo(
-					[]kueue.TopologyDomainAssignment{
-						{
-							Values: []string{"kind-worker"},
-							Count:  1,
-						},
-						{
-							Values: []string{"kind-worker2"},
-							Count:  1,
-						},
-						{
-							Values: []string{"kind-worker3"},
-							Count:  1,
-						},
-					},
-				))
+				gomega.Expect(createdWorkload.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
+					tas.V1Beta2From(&tas.TopologyAssignment{
+						Levels: []string{corev1.LabelHostname},
+						Domains: []tas.TopologyDomainAssignment{
+							{
+								Values: []string{"kind-worker"},
+								Count:  1,
+							},
+							{
+								Values: []string{"kind-worker2"},
+								Count:  1,
+							},
+							{
+								Values: []string{"kind-worker3"},
+								Count:  1,
+							},
+						}})))
 			})
 
 			ginkgo.By(fmt.Sprintf("verify the workload %q gets finished", wlLookupKey), func() {
diff --git a/test/integration/multikueue/tas/tas_test.go b/test/integration/multikueue/tas/tas_test.go
index e3a3baa47..78ebf3dc0 100644
--- a/test/integration/multikueue/tas/tas_test.go
+++ b/test/integration/multikueue/tas/tas_test.go
@@ -40,6 +40,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/admissionchecks/provisioning"
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -350,10 +351,10 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 					g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 					g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						tas.V1Beta2From(&tas.TopologyAssignment{
 							Levels:  []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"host-1"}}},
-						},
+							Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"host-1"}}},
+						}),
 					))
 				}, util.Timeout, util.Interval).Should(gomega.Succeed())
 			})
@@ -575,11 +576,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 					g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 					g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						tas.V1Beta2From(&tas.TopologyAssignment{
 							Levels: []string{
 								corev1.LabelHostname,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []tas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -587,7 +588,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				}, util.Timeout, util.Interval).Should(gomega.Succeed())
 			})
diff --git a/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go b/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
index 2011b69ad..fc51568d8 100644
--- a/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/appwrapper/appwrapper_controller_test.go
@@ -39,6 +39,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadaw "sigs.k8s.io/kueue/pkg/controller/jobs/appwrapper"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingaw "sigs.k8s.io/kueue/pkg/util/testingjobs/appwrapper"
@@ -946,10 +947,10 @@ var _ = ginkgo.Describe("AppWrapper controller with TopologyAwareScheduling", gi
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go b/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
index ca5866111..67cf231e9 100644
--- a/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/jaxjob/jaxjob_controller_test.go
@@ -34,6 +34,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadjaxjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/jaxjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjaxjob "sigs.k8s.io/kueue/pkg/util/testingjobs/jaxjob"
@@ -674,16 +675,16 @@ var _ = ginkgo.Describe("JAXJob controller with TopologyAwareScheduling", ginkgo
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/job/job_controller_test.go b/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
index c9c0097cd..a376dd344 100644
--- a/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/job/job_controller_test.go
@@ -45,6 +45,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -3113,10 +3114,10 @@ var _ = ginkgo.Describe("Job controller with TopologyAwareScheduling", ginkgo.Or
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
@@ -3165,10 +3166,10 @@ var _ = ginkgo.Describe("Job controller with TopologyAwareScheduling", ginkgo.Or
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go b/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
index 43b7d0550..42b295908 100644
--- a/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/jobset/jobset_controller_test.go
@@ -38,6 +38,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadjobset "sigs.k8s.io/kueue/pkg/controller/jobs/jobset"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjobset "sigs.k8s.io/kueue/pkg/util/testingjobs/jobset"
@@ -1281,16 +1282,16 @@ var _ = ginkgo.Describe("JobSet controller with TopologyAwareScheduling", ginkgo
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go b/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
index 7fd268284..c565fa4b1 100644
--- a/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/mpijob/mpijob_controller_test.go
@@ -37,6 +37,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadmpijob "sigs.k8s.io/kueue/pkg/controller/jobs/mpijob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -1012,16 +1013,16 @@ var _ = ginkgo.Describe("MPIJob controller with TopologyAwareScheduling", ginkgo
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go b/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
index b554f245f..8f0e633f9 100644
--- a/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/paddlejob/paddlejob_controller_test.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadpaddlejob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/paddlejob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -415,16 +416,16 @@ var _ = ginkgo.Describe("PaddleJob controller with TopologyAwareScheduling", fra
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go b/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
index 36f0ed51b..72c67603c 100644
--- a/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/pod/pod_controller_test.go
@@ -18,6 +18,7 @@ package pod
 
 import (
 	"fmt"
+	"slices"
 	"strconv"
 
 	"github.com/google/go-cmp/cmp"
@@ -39,6 +40,7 @@ import (
 	podcontroller "sigs.k8s.io/kueue/pkg/controller/jobs/pod"
 	podconstants "sigs.k8s.io/kueue/pkg/controller/jobs/pod/constants"
 	"sigs.k8s.io/kueue/pkg/features"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjob "sigs.k8s.io/kueue/pkg/util/testingjobs/job"
@@ -2288,10 +2290,10 @@ var _ = ginkgo.Describe("Pod controller with TopologyAwareScheduling", ginkgo.Or
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
@@ -2339,10 +2341,10 @@ var _ = ginkgo.Describe("Pod controller with TopologyAwareScheduling", ginkgo.Or
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{tasBlockLabel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 2, Values: []string{"b1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 2, Values: []string{"b1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
@@ -2486,8 +2488,9 @@ var _ = ginkgo.Describe("Pod controller with TASReplaceNodeOnPodTermination", gi
 		ginkgo.By("verify the workload is admitted", func() {
 			util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl)
 			gomega.Expect(k8sClient.Get(ctx, wlKey, wl)).To(gomega.Succeed())
-			gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).To(gomega.HaveLen(1))
-			nodeName = wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Values[0]
+			nodeNames := slices.Collect(tas.LowestLevelValues(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))
+			gomega.Expect(nodeNames).To(gomega.HaveLen(1))
+			nodeName = nodeNames[0]
 			gomega.Expect(nodeName).To(gomega.Or(gomega.Equal("x1"), gomega.Equal("x3")))
 		})
 
@@ -2531,8 +2534,9 @@ var _ = ginkgo.Describe("Pod controller with TASReplaceNodeOnPodTermination", gi
 		ginkgo.By("verify the workload is assigned a new node", func() {
 			gomega.Eventually(func(g gomega.Gomega) string {
 				gomega.Expect(k8sClient.Get(ctx, wlKey, wl)).To(gomega.Succeed())
-				gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).To(gomega.HaveLen(1))
-				return wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Values[0]
+				nodeNames := slices.Collect(tas.LowestLevelValues(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))
+				gomega.Expect(nodeNames).To(gomega.HaveLen(1))
+				return nodeNames[0]
 			}, util.Timeout, util.Interval).ShouldNot(gomega.Equal(nodeName))
 		})
 	})
@@ -2568,8 +2572,9 @@ var _ = ginkgo.Describe("Pod controller with TASReplaceNodeOnPodTermination", gi
 		ginkgo.By("verify the workload is admitted", func() {
 			util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl)
 			gomega.Expect(k8sClient.Get(ctx, wlKey, wl)).To(gomega.Succeed())
-			gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).To(gomega.HaveLen(1))
-			nodeName = wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Values[0]
+			nodeNames := slices.Collect(tas.LowestLevelValues(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))
+			gomega.Expect(nodeNames).To(gomega.HaveLen(1))
+			nodeName = nodeNames[0]
 			gomega.Expect(nodeName).To(gomega.Or(gomega.Equal("x1"), gomega.Equal("x3")))
 		})
 
@@ -2613,8 +2618,9 @@ var _ = ginkgo.Describe("Pod controller with TASReplaceNodeOnPodTermination", gi
 		ginkgo.By("verify the workload is assigned a new node", func() {
 			gomega.Eventually(func(g gomega.Gomega) string {
 				gomega.Expect(k8sClient.Get(ctx, wlKey, wl)).To(gomega.Succeed())
-				gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).To(gomega.HaveLen(1))
-				return wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Values[0]
+				nodeNames := slices.Collect(tas.LowestLevelValues(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))
+				gomega.Expect(nodeNames).To(gomega.HaveLen(1))
+				return nodeNames[0]
 			}, util.Timeout, util.Interval).ShouldNot(gomega.Equal(nodeName))
 		})
 	})
diff --git a/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go b/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
index 2cb35dedc..cf96631df 100644
--- a/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/pytorchjob/pytorchjob_controller_test.go
@@ -34,6 +34,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadpytorchjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/pytorchjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -711,16 +712,16 @@ var _ = ginkgo.Describe("PyTorchJob controller with TopologyAwareScheduling", gi
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go b/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
index bfa13849e..8aa8afff2 100644
--- a/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/tfjob/tfjob_controller_test.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadtfjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/tfjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -445,22 +446,22 @@ var _ = ginkgo.Describe("TFJob controller with TopologyAwareScheduling", framewo
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(3))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
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 4f2c9a827..f83e664c7 100644
--- a/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/trainjob/trainjob_controller_test.go
@@ -37,6 +37,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/constants"
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadtrainjob "sigs.k8s.io/kueue/pkg/controller/jobs/trainjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingjobset "sigs.k8s.io/kueue/pkg/util/testingjobs/jobset"
@@ -711,16 +712,16 @@ var _ = ginkgo.Describe("TrainJob controller with TopologyAwareScheduling", gink
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go b/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
index 3a4b04ad9..25c5b89ee 100644
--- a/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
+++ b/test/integration/singlecluster/controller/jobs/xgboostjob/xgboostjob_controller_test.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/kueue/pkg/controller/jobframework"
 	workloadxgboostjob "sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/jobs/xgboostjob"
 	"sigs.k8s.io/kueue/pkg/controller/jobs/kubeflow/kubeflowjob"
+	"sigs.k8s.io/kueue/pkg/util/tas"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta2"
 	testingnode "sigs.k8s.io/kueue/pkg/util/testingjobs/node"
@@ -412,16 +413,16 @@ var _ = ginkgo.Describe("XGBoostJob controller with TopologyAwareScheduling", fr
 				g.Expect(wl.Status.Admission).ShouldNot(gomega.BeNil())
 				g.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(2))
 				g.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 				g.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-					&kueue.TopologyAssignment{
+					tas.V1Beta2From(&tas.TopologyAssignment{
 						Levels:  []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-						Domains: []kueue.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
-					},
+						Domains: []tas.TopologyDomainAssignment{{Count: 1, Values: []string{"b1", "r1"}}},
+					}),
 				))
 			}, util.Timeout, util.Interval).Should(gomega.Succeed())
 		})
diff --git a/test/integration/singlecluster/scheduler/fairsharing/suite_test.go b/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
index c02423a32..426f28e85 100644
--- a/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
+++ b/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
@@ -35,6 +35,7 @@ import (
 	workloadjob "sigs.k8s.io/kueue/pkg/controller/jobs/job"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/scheduler"
+	"sigs.k8s.io/kueue/pkg/scheduler/preemption/fairsharing"
 	"sigs.k8s.io/kueue/pkg/webhooks"
 	"sigs.k8s.io/kueue/test/integration/framework"
 	"sigs.k8s.io/kueue/test/util"
@@ -69,15 +70,13 @@ var _ = ginkgo.AfterSuite(func() {
 
 func managerAndSchedulerSetup(admissionFairSharing *config.AdmissionFairSharing) framework.ManagerSetup {
 	return func(ctx context.Context, mgr manager.Manager) {
-		fairSharing := &config.FairSharing{
-			Enable: true,
-		}
+		fairSharing := &config.FairSharing{}
 
 		err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
 		_ = features.SetEnable(features.AdmissionFairSharing, true)
-		cCache := schdcache.New(mgr.GetClient(), schdcache.WithFairSharing(fairSharing.Enable), schdcache.WithAdmissionFairSharing(admissionFairSharing))
+		cCache := schdcache.New(mgr.GetClient(), schdcache.WithFairSharing(fairsharing.Enabled(fairSharing)), schdcache.WithAdmissionFairSharing(admissionFairSharing))
 		queues := qcache.NewManager(mgr.GetClient(), cCache, qcache.WithAdmissionFairSharing(admissionFairSharing))
 
 		configuration := &config.Configuration{FairSharing: fairSharing, AdmissionFairSharing: admissionFairSharing}
diff --git a/test/integration/singlecluster/tas/tas_test.go b/test/integration/singlecluster/tas/tas_test.go
index fd606fc8a..a987800f9 100644
--- a/test/integration/singlecluster/tas/tas_test.go
+++ b/test/integration/singlecluster/tas/tas_test.go
@@ -339,12 +339,12 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the workload1", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultBlockTopologyLevel,
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -360,7 +360,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				})
 
@@ -381,12 +381,12 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the workload2", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl2), wl2)).To(gomega.Succeed())
 					gomega.Expect(wl2.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultBlockTopologyLevel,
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -395,7 +395,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				})
 
@@ -416,12 +416,12 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the wl3", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl3), wl3)).To(gomega.Succeed())
 					gomega.Expect(wl3.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultBlockTopologyLevel,
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -430,7 +430,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				})
 
@@ -457,12 +457,12 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the wl4", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl4), wl4)).To(gomega.Succeed())
 					gomega.Expect(wl4.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultBlockTopologyLevel,
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -471,7 +471,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -604,13 +604,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the workload", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl), wl)).To(gomega.Succeed())
 					gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"b1", "r1"}},
 								{Count: 1, Values: []string{"b1", "r2"}},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -673,13 +673,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the workload", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl), wl)).To(gomega.Succeed())
 					gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{utiltesting.DefaultBlockTopologyLevel, utiltesting.DefaultRackTopologyLevel},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"b1", "r1"}},
 								{Count: 1, Values: []string{"b1", "r2"}},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -837,13 +837,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -882,13 +882,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -902,13 +902,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					gomega.Eventually(func(g gomega.Gomega) {
 						g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{Count: 1, Values: []string{"x1"}},
 									{Count: 1, Values: []string{"x4"}},
 								},
-							},
+							}),
 						))
 						g.Expect(wl1.Status.UnhealthyNodes).NotTo(gomega.ContainElement(kueue.UnhealthyNode{Name: nodeName}))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
@@ -931,13 +931,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -956,13 +956,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					gomega.Eventually(func(g gomega.Gomega) {
 						g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{Count: 1, Values: []string{"x1"}},
 									{Count: 1, Values: []string{"x4"}},
 								},
-							},
+							}),
 						))
 						gomega.Expect(wl1.Status.UnhealthyNodes).NotTo(gomega.ContainElement(kueue.UnhealthyNode{Name: nodeName}))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
@@ -986,13 +986,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1050,13 +1050,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1104,13 +1104,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x3"}},
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1127,13 +1127,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl2)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl2), wl2)).To(gomega.Succeed())
 					gomega.Expect(wl2.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x4"}},
 								{Count: 1, Values: []string{"x2"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1152,13 +1152,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					gomega.Consistently(func(g gomega.Gomega) {
 						g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{Count: 1, Values: []string{"x3"}},
 									{Count: 1, Values: []string{"x1"}},
 								},
-							},
+							}),
 						))
 					}, util.ConsistentDuration, util.ShortInterval).Should(gomega.Succeed())
 				})
@@ -1170,13 +1170,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					gomega.Eventually(func(g gomega.Gomega) {
 						g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{Count: 1, Values: []string{"x1"}},
 									{Count: 1, Values: []string{"x4"}},
 								},
-							},
+							}),
 						))
 						g.Expect(wl1.Status.UnhealthyNodes).NotTo(gomega.ContainElement(kueue.UnhealthyNode{Name: nodeName}))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
@@ -1202,13 +1202,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{node1Name}},
 								{Count: 1, Values: []string{node2Name}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1252,13 +1252,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{node1Name}},
 								{Count: 1, Values: []string{node2Name}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1401,13 +1401,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x1"}},
 								{Count: 1, Values: []string{"x3"}},
 							},
-						},
+						}),
 					))
 				})
 
@@ -1436,13 +1436,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x2"}},
 								{Count: 1, Values: []string{"x4"}},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -1560,13 +1560,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 8, Values: []string{"x1"}},
 								{Count: 8, Values: []string{"x2"}},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -1597,22 +1597,22 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 					util.ExpectWorkloadsToBeAdmitted(ctx, k8sClient, wl1)
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 1, Values: []string{"x1"}},
 							},
-						},
+						}),
 					))
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{corev1.LabelHostname},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{Count: 10, Values: []string{"x1"}},
 								{Count: 10, Values: []string{"x2"}},
 								{Count: 10, Values: []string{"x3"}},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -2290,11 +2290,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						g.Expect(wl1.Status.Admission).ShouldNot(gomega.BeNil())
 						g.Expect(wl1.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeEquivalentTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{
 									corev1.LabelHostname,
 								},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{
 										Count: 1,
 										Values: []string{
@@ -2302,7 +2302,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 										},
 									},
 								},
-							},
+							}),
 						))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
 				})
@@ -2410,11 +2410,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						g.Expect(wl1.Status.Admission).ShouldNot(gomega.BeNil())
 						g.Expect(wl1.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeEquivalentTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{
 									corev1.LabelHostname,
 								},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{
 										Count: 1,
 										Values: []string{
@@ -2422,7 +2422,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 										},
 									},
 								},
-							},
+							}),
 						))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
 				})
@@ -2533,11 +2533,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						g.Expect(wl1.Status.Admission).ShouldNot(gomega.BeNil())
 						g.Expect(wl1.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeEquivalentTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{
 									corev1.LabelHostname,
 								},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{
 										Count: 1,
 										Values: []string{
@@ -2545,7 +2545,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 										},
 									},
 								},
-							},
+							}),
 						))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
 				})
@@ -2666,11 +2666,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						g.Expect(wl1.Status.Admission).ShouldNot(gomega.BeNil())
 						g.Expect(wl1.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeEquivalentTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{
 									corev1.LabelHostname,
 								},
-								Domains: []kueue.TopologyDomainAssignment{
+								Domains: []utiltas.TopologyDomainAssignment{
 									{
 										Count: 1,
 										Values: []string{
@@ -2678,7 +2678,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 										},
 									},
 								},
-							},
+							}),
 						))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
 				})
@@ -2810,13 +2810,13 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						g.Expect(wl1.Status.Admission).ShouldNot(gomega.BeNil())
 						g.Expect(wl1.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						g.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeEquivalentTo(
-							&kueue.TopologyAssignment{
+							utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 								Levels: []string{corev1.LabelHostname},
-								Domains: []kueue.TopologyDomainAssignment{{
+								Domains: []utiltas.TopologyDomainAssignment{{
 									Count:  1,
 									Values: []string{"x1"},
 								}},
-							},
+							}),
 						))
 					}, util.Timeout, util.Interval).Should(gomega.Succeed())
 
@@ -2938,11 +2938,11 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 				ginkgo.By("verify admission for the workload1", func() {
 					gomega.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl1), wl1)).To(gomega.Succeed())
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[0].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 1,
 									Values: []string{
@@ -2950,14 +2950,14 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 					gomega.Expect(wl1.Status.Admission.PodSetAssignments[1].TopologyAssignment).Should(gomega.BeComparableTo(
-						&kueue.TopologyAssignment{
+						utiltas.V1Beta2From(&utiltas.TopologyAssignment{
 							Levels: []string{
 								utiltesting.DefaultRackTopologyLevel,
 							},
-							Domains: []kueue.TopologyDomainAssignment{
+							Domains: []utiltas.TopologyDomainAssignment{
 								{
 									Count: 2,
 									Values: []string{
@@ -2965,7 +2965,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 									},
 								},
 							},
-						},
+						}),
 					))
 				})
 			})
@@ -3260,8 +3260,7 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						gomega.Expect(wl.Status.Admission.PodSetAssignments).Should(gomega.HaveLen(1))
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).ShouldNot(gomega.BeNil())
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Levels).Should(gomega.Equal([]string{corev1.LabelHostname}))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).Should(gomega.HaveLen(1))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Count).Should(gomega.Equal(int32(1)))
+						gomega.Expect(slices.Collect(utiltas.PodCounts(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))).To(gomega.Equal([]int32{1}))
 					})
 				})
 			})
@@ -3428,14 +3427,12 @@ var _ = ginkgo.Describe("Topology Aware Scheduling", ginkgo.Ordered, func() {
 						// First pod set (8 GPUs) should be assigned to a single node
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment).ShouldNot(gomega.BeNil())
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Levels).Should(gomega.Equal([]string{corev1.LabelHostname}))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains).Should(gomega.HaveLen(1))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment.Domains[0].Count).Should(gomega.Equal(int32(1)))
+						gomega.Expect(slices.Collect(utiltas.PodCounts(wl.Status.Admission.PodSetAssignments[0].TopologyAssignment))).To(gomega.Equal([]int32{1}))
 
 						// Second pod set (1 GPU) should be assigned to a single node
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment).ShouldNot(gomega.BeNil())
 						gomega.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment.Levels).Should(gomega.Equal([]string{corev1.LabelHostname}))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment.Domains).Should(gomega.HaveLen(1))
-						gomega.Expect(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment.Domains[0].Count).Should(gomega.Equal(int32(1)))
+						gomega.Expect(slices.Collect(utiltas.PodCounts(wl.Status.Admission.PodSetAssignments[1].TopologyAssignment))).To(gomega.Equal([]int32{1}))
 					})
 
 					ginkgo.By("Demonstrating TAS prevents resource waste by allowing schedulable workloads", func() {