Repository: kubernetes-sigs/gateway-api-inference-extension

Issue:
Feature request: (helm) add custom volume and volumeMount support for epp-deployment
<!-- Please only use this template for submitting enhancement requests -->

**What would you like to be added**:
We would like to request support for adding custom volumes and volumeMounts to the epp container in the inferencepool helm chart template.
**Why is this needed**:
Currently, in the llm-d-kv-cache-manager project, which precise prefix-cache aware routing relies on, two approaches for tokenizing requests have been proposed:
1. Mount a Python-based sidecar to the EPP (External Processing Proxy). This sidecar can communicate with the EPP via UDS (Unix Domain Socket) to achieve more flexible and compatible tokenization. For more context, see: https://github.com/llm-d/llm-d-kv-cache-manager/pull/137.
2. The EPP can search for cached tokenizer configurations in the local path to avoid downloading from Hugging Face every time it starts. For more context, see: https://github.com/llm-d/llm-d-kv-cache-manager/pull/142.

Both approaches introduce the need for adding custom volumes and volumeMounts for new EPP plugins, and showcase potential use cases for this requirement:
1. An EPP plugin may use some mounted data to perform its tasks (e.g., pre-downloaded tokenizer weights).
2. An EPP plugin can share an emptyDir volume with the EPP sidecar, enabling communication between them via UDS.

Additionally, there is an extra requirement: making the epp sidecar configmap in the current epp-config optional. This would enhance the versatility of EPP sidecar deployments, especially considering the emerging need to support different EPP plugin functionalities through various EPP sidecars.

cc @vMaroon 

Hints from issue comments:

Solution patch:
diff --git a/cmd/epp/runner/runner.go b/cmd/epp/runner/runner.go
index e78b687..9882d5a 100644
--- a/cmd/epp/runner/runner.go
+++ b/cmd/epp/runner/runner.go
@@ -60,6 +60,7 @@ import (
 	dlmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer/metrics"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datastore"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/contracts"
 	fccontroller "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/controller"
 	fcregistry "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/registry"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/metrics"
@@ -330,7 +331,10 @@ func (r *Runner) Run(ctx context.Context) error {
 
 	// --- Admission Control Initialization ---
 	var admissionController requestcontrol.AdmissionController
+	var locator contracts.PodLocator
+	locator = requestcontrol.NewDatastorePodLocator(ds)
 	if r.featureGates[flowcontrol.FeatureGate] {
+		locator = requestcontrol.NewCachedPodLocator(ctx, locator, time.Millisecond*50)
 		setupLog.Info("Initializing experimental Flow Control layer")
 		fcCfg, err := flowControlConfig.ValidateAndApplyDefaults()
 		if err != nil {
@@ -342,24 +346,28 @@ func (r *Runner) Run(ctx context.Context) error {
 		if err != nil {
 			return fmt.Errorf("failed to initialize Flow Registry: %w", err)
 		}
-		fc, err := fccontroller.NewFlowController(ctx, fcCfg.Controller, registry, saturationDetector, setupLog)
+		fc, err := fccontroller.NewFlowController(
+			ctx,
+			fcCfg.Controller,
+			registry, saturationDetector,
+			locator,
+			setupLog,
+		)
 		if err != nil {
 			return fmt.Errorf("failed to initialize Flow Controller: %w", err)
 		}
 		go registry.Run(ctx)
-		admissionController = requestcontrol.NewFlowControlAdmissionController(saturationDetector, fc)
+		admissionController = requestcontrol.NewFlowControlAdmissionController(fc)
 	} else {
 		setupLog.Info("Experimental Flow Control layer is disabled, using legacy admission control")
-		admissionController = requestcontrol.NewLegacyAdmissionController(saturationDetector)
+		admissionController = requestcontrol.NewLegacyAdmissionController(saturationDetector, locator)
 	}
 
-	locator := requestcontrol.NewDatastorePodLocator(ds)
-	cachedLocator := requestcontrol.NewCachedPodLocator(ctx, locator, time.Millisecond*50)
 	director := requestcontrol.NewDirectorWithConfig(
 		ds,
 		scheduler,
 		admissionController,
-		cachedLocator,
+		locator,
 		r.requestControlConfig)
 
 	// --- Setup ExtProc Server Runner ---
@@ -472,7 +480,7 @@ func (r *Runner) parseConfigurationPhaseOne(ctx context.Context) (*configapi.End
 
 	r.registerInTreePlugins()
 
-	rawConfig, featureGates, err := loader.LoadConfigPhaseOne(configBytes, logger)
+	rawConfig, featureGates, err := loader.LoadRawConfig(configBytes, logger)
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse config - %w", err)
 	}
@@ -498,7 +506,7 @@ func makePodListFunc(ds datastore.Datastore) func() []types.NamespacedName {
 func (r *Runner) parseConfigurationPhaseTwo(ctx context.Context, rawConfig *configapi.EndpointPickerConfig, ds datastore.Datastore) (*config.Config, error) {
 	logger := log.FromContext(ctx)
 	handle := plugins.NewEppHandle(ctx, makePodListFunc(ds))
-	cfg, err := loader.LoadConfigPhaseTwo(rawConfig, handle, logger)
+	cfg, err := loader.InstantiateAndConfigure(rawConfig, handle, logger)
 
 	if err != nil {
 		return nil, fmt.Errorf("failed to load the configuration - %w", err)
diff --git a/config/charts/inferencepool/README.md b/config/charts/inferencepool/README.md
index 769b964..013a1f2 100644
--- a/config/charts/inferencepool/README.md
+++ b/config/charts/inferencepool/README.md
@@ -247,6 +247,22 @@ The following table list the configurable parameters of the chart.
 | `inferenceExtension.tracing.otelExporterEndpoint`          | OpenTelemetry collector endpoint.                                                                                                                                                                                                                  |
 | `inferenceExtension.tracing.sampling.sampler`              | The trace sampler to use. Currently, only `parentbased_traceidratio` is supported. This sampler respects the parent span’s sampling decision when present, and applies the configured ratio for root spans.                                        |
 | `inferenceExtension.tracing.sampling.samplerArg`           | Sampler-specific argument. For `parentbased_traceidratio`, this defines the base sampling rate for new traces (root spans), as a float string in the range [0.0, 1.0]. For example, "0.1" enables 10% sampling.                                    |
+| `inferenceExtension.volumes`                               | List of volumes to mount in the EPP deployment as free-form YAML. Optional.                                                                                                                                                                       |
+| `inferenceExtension.volumeMounts`                          | List of volume mounts for the EPP container as free-form YAML. Optional.                                                                                                                                                                          |
+| `inferenceExtension.sidecar.enabled`                       | Enables or disables the sidecar container in the EPP deployment. Defaults to `false`.                                                                                                                                                             |
+| `inferenceExtension.sidecar.name`                          | Name of the sidecar container. Required when the sidecar is enabled.                                                                                                                                                                              |
+| `inferenceExtension.sidecar.image`                         | Image for the sidecar container. Required when the sidecar is enabled.                                                                                                                                                                            |
+| `inferenceExtension.sidecar.imagePullPolicy`               | Image pull policy for the sidecar container. Possible values: `Always`, `IfNotPresent`, or `Never`. Defaults to `IfNotPresent`.                                                                                                                  |
+| `inferenceExtension.sidecar.command`                       | Command to run in the sidecar container as a single string. Optional.                                                                                                                                                                             |
+| `inferenceExtension.sidecar.args`                          | Arguments to pass to the command in the sidecar container as a list of strings. Optional.                                                                                                                                                         |
+| `inferenceExtension.sidecar.env`                           | Environment variables to set in the sidecar container as free-form YAML. Optional.                                                                                                                                                                |
+| `inferenceExtension.sidecar.ports`                         | List of ports to expose for the sidecar container. Optional.                                                                                                                                                                                      |
+| `inferenceExtension.sidecar.livenessProbe`                 | Liveness probe configuration for the sidecar container. Optional.                                                                                                                                                                                 |
+| `inferenceExtension.sidecar.readinessProbe`                | Readiness probe configuration for the sidecar container. Optional.                                                                                                                                                                                |
+| `inferenceExtension.sidecar.resources`                     | Resource limits and requests for the sidecar container. Optional.                                                                                                                                                                                 |
+| `inferenceExtension.sidecar.volumeMounts`                  | List of volume mounts for the sidecar container. Optional.                                                                                                                                                                                        |
+| `inferenceExtension.sidecar.volumes`                       | List of volumes for the sidecar container. Optional.                                                                                                                                                                                              |
+| `inferenceExtension.sidecar.configMapData`                 | Custom key-value pairs to be included in a ConfigMap created for the sidecar container. Only used when `inferenceExtension.sidecar.enabled` is `true`. Optional.                                                                                           |
 | `provider.name`                                            | Name of the Inference Gateway implementation being used. Possible values: [`none`, `gke`, or `istio`]. Defaults to `none`.                                                                                                                         |
 | `provider.gke.autopilot`                                   | Set to `true` if the cluster is a GKE Autopilot cluster. This is only used if `provider.name` is `gke`. Defaults to `false`.                                                                                                                       |
 
diff --git a/config/charts/inferencepool/templates/epp-config.yaml b/config/charts/inferencepool/templates/epp-config.yaml
index e082db7..f34d5cf 100644
--- a/config/charts/inferencepool/templates/epp-config.yaml
+++ b/config/charts/inferencepool/templates/epp-config.yaml
@@ -64,14 +64,14 @@ data:
   {{- end }}
   
 ---
-{{- if .Values.inferenceExtension.sidecar.enabled }}
+{{- if and .Values.inferenceExtension.sidecar.enabled .Values.inferenceExtension.sidecar.configMapData }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
-  name: {{ .Values.inferenceExtension.sidecar.configMap.name }}
+  name: {{ include "gateway-api-inference-extension.name" . }}-sidecar
   namespace: {{ .Release.Namespace }}
 data:
-  {{- .Values.inferenceExtension.sidecar.configMap.data | toYaml | nindent 2 }}
+  {{- .Values.inferenceExtension.sidecar.configMapData | toYaml | nindent 2 }}
 {{- end }}
 ---
 {{- if .Values.inferenceExtension.latencyPredictor.enabled }}
diff --git a/config/charts/inferencepool/templates/epp-deployment.yaml b/config/charts/inferencepool/templates/epp-deployment.yaml
index f792874..b58c3b7 100644
--- a/config/charts/inferencepool/templates/epp-deployment.yaml
+++ b/config/charts/inferencepool/templates/epp-deployment.yaml
@@ -182,8 +182,14 @@ spec:
         volumeMounts:
         - name: plugins-config-volume
           mountPath: "/config"
+        {{- if .Values.inferenceExtension.volumeMounts }}
+        {{- toYaml .Values.inferenceExtension.volumeMounts | nindent 8 }}
+        {{- end }}
       {{- include "gateway-api-inference-extension.latencyPredictor.containers" . | nindent 6 }}
       volumes:
+      {{- if .Values.inferenceExtension.volumes }}
+      {{- toYaml .Values.inferenceExtension.volumes | nindent 6 }}
+      {{- end }}
       {{- if .Values.inferenceExtension.sidecar.volumes }}
       {{- tpl (toYaml .Values.inferenceExtension.sidecar.volumes) $ | nindent 6 }}
       {{- end }}
diff --git a/go.mod b/go.mod
index 5a7cdb4..553af66 100644
--- a/go.mod
+++ b/go.mod
@@ -11,20 +11,20 @@ require (
 	github.com/google/go-cmp v0.7.0
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/golang-lru/v2 v2.0.7
-	github.com/onsi/ginkgo/v2 v2.27.2
-	github.com/onsi/gomega v1.38.2
+	github.com/onsi/ginkgo/v2 v2.27.3
+	github.com/onsi/gomega v1.38.3
 	github.com/prometheus/client_golang v1.23.2
 	github.com/prometheus/client_model v0.6.2
 	github.com/prometheus/common v0.67.4
 	github.com/prometheus/prometheus v0.307.3
 	github.com/stretchr/testify v1.11.1
-	go.opentelemetry.io/otel v1.38.0
+	go.opentelemetry.io/otel v1.39.0
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
-	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0
-	go.opentelemetry.io/otel/sdk v1.38.0
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0
+	go.opentelemetry.io/otel/sdk v1.39.0
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.27.1
-	golang.org/x/sync v0.18.0
+	golang.org/x/sync v0.19.0
 	google.golang.org/grpc v1.77.0
 	google.golang.org/protobuf v1.36.10
 	k8s.io/api v0.34.2
@@ -101,8 +101,8 @@ require (
 	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
-	go.opentelemetry.io/otel/metric v1.38.0 // indirect
-	go.opentelemetry.io/otel/trace v1.38.0 // indirect
+	go.opentelemetry.io/otel/metric v1.39.0 // indirect
+	go.opentelemetry.io/otel/trace v1.39.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.7.1 // indirect
 	go.uber.org/atomic v1.11.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.3 // indirect
@@ -112,7 +112,7 @@ require (
 	golang.org/x/mod v0.29.0 // indirect
 	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
 	golang.org/x/oauth2 v0.32.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
+	golang.org/x/sys v0.39.0 // indirect
 	golang.org/x/term v0.36.0 // indirect
 	golang.org/x/text v0.30.0 // indirect
 	golang.org/x/time v0.13.0 // indirect
diff --git a/go.sum b/go.sum
index 9d31894..84f7a49 100644
--- a/go.sum
+++ b/go.sum
@@ -208,10 +208,10 @@ github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
 github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
-github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
-github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
-github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
+github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
+github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
+github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
+github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -272,22 +272,22 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
 go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
-go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
-go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
+go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
-go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
-go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
-go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
-go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
-go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
-go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
-go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
-go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g=
+go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
+go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
+go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
+go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
+go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
+go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
+go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
+go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
 go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
 go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
 go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -324,13 +324,13 @@ golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwE
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
-golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
-golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
 golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/pkg/epp/config/loader/configloader.go b/pkg/epp/config/loader/configloader.go
index 56f05dc..03126f3 100644
--- a/pkg/epp/config/loader/configloader.go
+++ b/pkg/epp/config/loader/configloader.go
@@ -35,171 +35,192 @@ import (
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/profile"
 )
 
-var scheme = runtime.NewScheme()
-
-var registeredFeatureGates = sets.New[string]() // set of feature gates names, a name must be unique
+var (
+	scheme                 = runtime.NewScheme()
+	registeredFeatureGates = sets.New[string]()
+)
 
 func init() {
 	utilruntime.Must(configapi.Install(scheme))
 }
 
-// LoadConfigPhaseOne first phase of loading configuration from supplied text that was converted to []byte
-func LoadConfigPhaseOne(configBytes []byte, logger logr.Logger) (*configapi.EndpointPickerConfig, map[string]bool, error) {
-	rawConfig, err := loadRawConfig(configBytes)
+// RegisterFeatureGate registers a feature gate name for validation purposes.
+func RegisterFeatureGate(gate string) {
+	registeredFeatureGates.Insert(gate)
+}
+
+// LoadRawConfig parses the raw configuration bytes, applies initial defaults, and extracts feature gates.
+// It does not instantiate plugins.
+func LoadRawConfig(configBytes []byte, logger logr.Logger) (*configapi.EndpointPickerConfig, map[string]bool, error) {
+	rawConfig, err := decodeRawConfig(configBytes)
 	if err != nil {
 		return nil, nil, err
 	}
+	logger.Info("Loaded raw configuration", "config", rawConfig)
 
-	logger.Info("Loaded configuration", "config", rawConfig)
+	applyStaticDefaults(rawConfig)
 
-	if err = validateFeatureGates(rawConfig.FeatureGates); err != nil {
-		return nil, nil, fmt.Errorf("failed to validate feature gates - %w", err)
+	// We validate gates early because they might dictate downstream loading logic.
+	if err := validateFeatureGates(rawConfig.FeatureGates); err != nil {
+		return nil, nil, fmt.Errorf("feature gate validation failed: %w", err)
 	}
 
-	setDefaultsPhaseOne(rawConfig)
-
 	featureConfig := loadFeatureConfig(rawConfig.FeatureGates)
-
 	return rawConfig, featureConfig, nil
 }
 
-// LoadConfigPhaseOne first phase of loading configuration from supplied text that was converted to []byte
-func LoadConfigPhaseTwo(rawConfig *configapi.EndpointPickerConfig, handle plugins.Handle, logger logr.Logger) (*config.Config, error) {
-	var err error
-	// instantiate loaded plugins
-	if err = instantiatePlugins(rawConfig.Plugins, handle); err != nil {
-		return nil, fmt.Errorf("failed to instantiate plugins - %w", err)
-	}
-
-	setDefaultsPhaseTwo(rawConfig, handle)
+// InstantiateAndConfigure performs the heavy lifting of plugin instantiation, system architecture injection, and
+// scheduler construction.
+func InstantiateAndConfigure(
+	rawConfig *configapi.EndpointPickerConfig,
+	handle plugins.Handle,
+	logger logr.Logger,
+) (*config.Config, error) {
 
-	logger.Info("Configuration with defaults set", "config", rawConfig)
+	if err := instantiatePlugins(rawConfig.Plugins, handle); err != nil {
+		return nil, fmt.Errorf("plugin instantiation failed: %w", err)
+	}
 
-	if err = validateSchedulingProfiles(rawConfig); err != nil {
-		return nil, fmt.Errorf("failed to validate scheduling profiles - %w", err)
+	if err := applySystemDefaults(rawConfig, handle); err != nil {
+		return nil, fmt.Errorf("system default application failed: %w", err)
 	}
+	logger.Info("Effective configuration loaded", "config", rawConfig)
 
-	config := &config.Config{}
+	if err := validateConfig(rawConfig); err != nil {
+		return nil, fmt.Errorf("configuration validation failed: %w", err)
+	}
 
-	config.SchedulerConfig, err = loadSchedulerConfig(rawConfig.SchedulingProfiles, handle)
+	schedulerConfig, err := buildSchedulerConfig(rawConfig.SchedulingProfiles, handle)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("scheduler config build failed: %w", err)
 	}
-	config.SaturationDetectorConfig = loadSaturationDetectorConfig(rawConfig.SaturationDetector)
 
-	return config, nil
+	return &config.Config{
+		SchedulerConfig:          schedulerConfig,
+		SaturationDetectorConfig: buildSaturationConfig(rawConfig.SaturationDetector),
+	}, nil
 }
 
-func loadRawConfig(configBytes []byte) (*configapi.EndpointPickerConfig, error) {
-	rawConfig := &configapi.EndpointPickerConfig{}
-
+func decodeRawConfig(configBytes []byte) (*configapi.EndpointPickerConfig, error) {
+	cfg := &configapi.EndpointPickerConfig{}
 	codecs := serializer.NewCodecFactory(scheme, serializer.EnableStrict)
-	err := runtime.DecodeInto(codecs.UniversalDecoder(), configBytes, rawConfig)
-	if err != nil {
-		return nil, fmt.Errorf("the configuration is invalid - %w", err)
+	if err := runtime.DecodeInto(codecs.UniversalDecoder(), configBytes, cfg); err != nil {
+		return nil, fmt.Errorf("failed to decode configuration JSON/YAML: %w", err)
 	}
-	return rawConfig, nil
+	return cfg, nil
 }
 
-func loadSchedulerConfig(configProfiles []configapi.SchedulingProfile, handle plugins.Handle) (*scheduling.SchedulerConfig, error) {
-	profiles := map[string]*framework.SchedulerProfile{}
-	for _, namedProfile := range configProfiles {
-		profile := framework.NewSchedulerProfile()
-		for _, plugin := range namedProfile.Plugins {
-			referencedPlugin := handle.Plugin(plugin.PluginRef)
-			if scorer, ok := referencedPlugin.(framework.Scorer); ok {
-				referencedPlugin = framework.NewWeightedScorer(scorer, *plugin.Weight)
+func instantiatePlugins(configuredPlugins []configapi.PluginSpec, handle plugins.Handle) error {
+	pluginNames := sets.New[string]()
+	for _, spec := range configuredPlugins {
+		if spec.Type == "" {
+			return fmt.Errorf("plugin '%s' is missing a type", spec.Name)
+		}
+		if pluginNames.Has(spec.Name) {
+			return fmt.Errorf("duplicate plugin name '%s'", spec.Name)
+		}
+		pluginNames.Insert(spec.Name)
+
+		factory, ok := plugins.Registry[spec.Type]
+		if !ok {
+			return fmt.Errorf("plugin type '%s' is not registered", spec.Type)
+		}
+
+		plugin, err := factory(spec.Name, spec.Parameters, handle)
+		if err != nil {
+			return fmt.Errorf("failed to create plugin '%s' (type: %s): %w", spec.Name, spec.Type, err)
+		}
+
+		handle.AddPlugin(spec.Name, plugin)
+	}
+	return nil
+}
+
+func buildSchedulerConfig(
+	configProfiles []configapi.SchedulingProfile,
+	handle plugins.Handle,
+) (*scheduling.SchedulerConfig, error) {
+
+	profiles := make(map[string]*framework.SchedulerProfile)
+
+	for _, cfgProfile := range configProfiles {
+		fwProfile := framework.NewSchedulerProfile()
+
+		for _, pluginRef := range cfgProfile.Plugins {
+			plugin := handle.Plugin(pluginRef.PluginRef)
+			if plugin == nil { // Should be caught by validation, but defensive check.
+				return nil, fmt.Errorf(
+					"plugin '%s' referenced in profile '%s' not found in handle",
+					pluginRef.PluginRef, cfgProfile.Name)
 			}
-			if err := profile.AddPlugins(referencedPlugin); err != nil {
-				return nil, fmt.Errorf("failed to load scheduler config - %w", err)
+
+			// Wrap Scorers with weights.
+			if scorer, ok := plugin.(framework.Scorer); ok {
+				weight := DefaultScorerWeight
+				if pluginRef.Weight != nil {
+					weight = *pluginRef.Weight
+				}
+				plugin = framework.NewWeightedScorer(scorer, weight)
+			}
+
+			if err := fwProfile.AddPlugins(plugin); err != nil {
+				return nil, fmt.Errorf("failed to add plugin '%s' to profile '%s': %w", pluginRef.PluginRef, cfgProfile.Name, err)
 			}
 		}
-		profiles[namedProfile.Name] = profile
+		profiles[cfgProfile.Name] = fwProfile
 	}
 
 	var profileHandler framework.ProfileHandler
-	for pluginName, plugin := range handle.GetAllPluginsWithNames() {
-		if theProfileHandler, ok := plugin.(framework.ProfileHandler); ok {
+	for name, plugin := range handle.GetAllPluginsWithNames() {
+		if ph, ok := plugin.(framework.ProfileHandler); ok {
 			if profileHandler != nil {
-				return nil, fmt.Errorf("only one profile handler is allowed. Both %s and %s are profile handlers", profileHandler.TypedName().Name, pluginName)
+				return nil, fmt.Errorf("multiple profile handlers found ('%s', '%s'); only one is allowed",
+					profileHandler.TypedName().Name, name)
 			}
-			profileHandler = theProfileHandler
+			profileHandler = ph
 		}
 	}
+
 	if profileHandler == nil {
-		return nil, errors.New("no profile handler was specified")
+		return nil, errors.New("no profile handler configured")
 	}
 
 	if profileHandler.TypedName().Type == profile.SingleProfileHandlerType && len(profiles) > 1 {
-		return nil, errors.New("single profile handler is intended to be used with a single profile, but multiple profiles were specified")
+		return nil, errors.New("SingleProfileHandler cannot support multiple scheduling profiles")
 	}
 
 	return scheduling.NewSchedulerConfig(profileHandler, profiles), nil
 }
 
-func loadFeatureConfig(featureGates configapi.FeatureGates) map[string]bool {
-	featureConfig := map[string]bool{}
-
+func loadFeatureConfig(gates configapi.FeatureGates) map[string]bool {
+	config := make(map[string]bool, len(registeredFeatureGates))
 	for gate := range registeredFeatureGates {
-		featureConfig[gate] = false
+		config[gate] = false
 	}
-
-	for _, gate := range featureGates {
-		featureConfig[gate] = true
+	for _, gate := range gates {
+		config[gate] = true
 	}
-
-	return featureConfig
+	return config
 }
 
-func loadSaturationDetectorConfig(sd *configapi.SaturationDetector) *saturationdetector.Config {
-	sdConfig := saturationdetector.Config{}
-
-	sdConfig.QueueDepthThreshold = sd.QueueDepthThreshold
-	if sdConfig.QueueDepthThreshold <= 0 {
-		sdConfig.QueueDepthThreshold = saturationdetector.DefaultQueueDepthThreshold
-	}
-	sdConfig.KVCacheUtilThreshold = sd.KVCacheUtilThreshold
-	if sdConfig.KVCacheUtilThreshold <= 0.0 || sdConfig.KVCacheUtilThreshold >= 1.0 {
-		sdConfig.KVCacheUtilThreshold = saturationdetector.DefaultKVCacheUtilThreshold
+func buildSaturationConfig(apiConfig *configapi.SaturationDetector) *saturationdetector.Config {
+	cfg := &saturationdetector.Config{
+		QueueDepthThreshold:       saturationdetector.DefaultQueueDepthThreshold,
+		KVCacheUtilThreshold:      saturationdetector.DefaultKVCacheUtilThreshold,
+		MetricsStalenessThreshold: saturationdetector.DefaultMetricsStalenessThreshold,
 	}
-	sdConfig.MetricsStalenessThreshold = sd.MetricsStalenessThreshold.Duration
-	if sdConfig.MetricsStalenessThreshold <= 0.0 {
-		sdConfig.MetricsStalenessThreshold = saturationdetector.DefaultMetricsStalenessThreshold
-	}
-
-	return &sdConfig
-}
 
-func instantiatePlugins(configuredPlugins []configapi.PluginSpec, handle plugins.Handle) error {
-	pluginNames := sets.New[string]() // set of plugin names, a name must be unique
-
-	for _, pluginConfig := range configuredPlugins {
-		if pluginConfig.Type == "" {
-			return fmt.Errorf("plugin definition for '%s' is missing a type", pluginConfig.Name)
+	if apiConfig != nil {
+		if apiConfig.QueueDepthThreshold > 0 {
+			cfg.QueueDepthThreshold = apiConfig.QueueDepthThreshold
 		}
-
-		if pluginNames.Has(pluginConfig.Name) {
-			return fmt.Errorf("plugin name '%s' used more than once", pluginConfig.Name)
+		if apiConfig.KVCacheUtilThreshold > 0.0 && apiConfig.KVCacheUtilThreshold < 1.0 {
+			cfg.KVCacheUtilThreshold = apiConfig.KVCacheUtilThreshold
 		}
-		pluginNames.Insert(pluginConfig.Name)
-
-		factory, ok := plugins.Registry[pluginConfig.Type]
-		if !ok {
-			return fmt.Errorf("plugin type '%s' is not found in registry", pluginConfig.Type)
-		}
-
-		plugin, err := factory(pluginConfig.Name, pluginConfig.Parameters, handle)
-		if err != nil {
-			return fmt.Errorf("failed to instantiate the plugin type '%s' - %w", pluginConfig.Type, err)
+		if apiConfig.MetricsStalenessThreshold.Duration > 0 {
+			cfg.MetricsStalenessThreshold = apiConfig.MetricsStalenessThreshold.Duration
 		}
-
-		handle.AddPlugin(pluginConfig.Name, plugin)
 	}
 
-	return nil
-}
-
-// RegisterFeatureGate registers feature gate keys for validation
-func RegisterFeatureGate(gate string) {
-	registeredFeatureGates.Insert(gate)
+	return cfg
 }
diff --git a/pkg/epp/config/loader/defaults.go b/pkg/epp/config/loader/defaults.go
index 224ffc5..d20f0d0 100644
--- a/pkg/epp/config/loader/defaults.go
+++ b/pkg/epp/config/loader/defaults.go
@@ -17,140 +17,146 @@ limitations under the License.
 package loader
 
 import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"fmt"
+
 	configapi "sigs.k8s.io/gateway-api-inference-extension/apix/config/v1alpha1"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
-	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/saturationdetector"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/picker"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/profile"
 )
 
-const (
-	// DefaultScorerWeight is the weight used for scorers referenced in the
-	// configuration without explicit weights.
-	DefaultScorerWeight = 1
-)
-
-// The code below sets the defaults in the configuration. It is done in two parts:
-//   1) Before the plugins are instantiated, for those defaults that can be set
-//      without knowing the concrete type of plugins
-//   2) After the plugins are instantiated, for things that one needs to know the
-//      concrete type of the plugins
+// DefaultScorerWeight is the weight used for scorers referenced in the configuration without explicit weights.
+const DefaultScorerWeight = 1
 
 var defaultScorerWeight = DefaultScorerWeight
 
-// setDefaultsPhaseOne Performs the first phase of setting configuration defaults.
-// In particuylar it:
-//  1. Sets the name of plugins, for which one wasn't specified
-//  2. Sets defaults for the feature gates
-//  3. Sets defaults for the SaturationDetector configuration
-func setDefaultsPhaseOne(cfg *configapi.EndpointPickerConfig) {
-	// If no name was given for the plugin, use it's type as the name
+// applyStaticDefaults sanitizes the configuration object before plugin instantiation.
+// It handles "Static" defaults: simple structural changes to the API object that do not require access to the plugin
+// registry.
+func applyStaticDefaults(cfg *configapi.EndpointPickerConfig) {
 	for idx, pluginConfig := range cfg.Plugins {
 		if pluginConfig.Name == "" {
 			cfg.Plugins[idx].Name = pluginConfig.Type
 		}
 	}
 
-	// If no feature gates were specified, provide a default FeatureGates struct
 	if cfg.FeatureGates == nil {
 		cfg.FeatureGates = configapi.FeatureGates{}
 	}
-
-	// If the SaturationDetector configuration wasn't specified setup a default one
-	if cfg.SaturationDetector == nil {
-		cfg.SaturationDetector = &configapi.SaturationDetector{}
-	}
-	if cfg.SaturationDetector.QueueDepthThreshold == 0 {
-		cfg.SaturationDetector.QueueDepthThreshold = saturationdetector.DefaultQueueDepthThreshold
-	}
-	if cfg.SaturationDetector.KVCacheUtilThreshold == 0.0 {
-		cfg.SaturationDetector.KVCacheUtilThreshold = saturationdetector.DefaultKVCacheUtilThreshold
-	}
-	if cfg.SaturationDetector.MetricsStalenessThreshold.Duration == 0.0 {
-		cfg.SaturationDetector.MetricsStalenessThreshold =
-			metav1.Duration{Duration: saturationdetector.DefaultMetricsStalenessThreshold}
-	}
 }
 
-// setDefaultsPhaseTwo Performs the second phase of setting configuration defaults.
-// In particular it:
-//  1. Adds a default SchedulingProfile if one wasn't specified.
-//  2. Adds an instance of the SingleProfileHandler, if no profile handler was
-//     specified and the configuration has only one SchedulingProfile
-//  3. Sets a default weight for all scorers without a weight
-//  4. Adds a picker (MaxScorePicker) to all SchedulingProfiles that don't have a picker
-func setDefaultsPhaseTwo(cfg *configapi.EndpointPickerConfig, handle plugins.Handle) {
+// applySystemDefaults injects required components that were omitted from the config.
+// It handles "System" defaults: logic that requires inspecting instantiated plugins (via the handle) to ensure the
+// system graph is complete.
+func applySystemDefaults(cfg *configapi.EndpointPickerConfig, handle plugins.Handle) error {
 	allPlugins := handle.GetAllPluginsWithNames()
+	if err := ensureSchedulingLayer(cfg, handle, allPlugins); err != nil {
+		return fmt.Errorf("failed to apply scheduling system defaults: %w", err)
+	}
+	return nil
+}
 
-	// If No SchedulerProfiles were specified in the confguration,
-	// create one named default with references to all of the scheduling
-	// plugins mentioned in the Plugins section of the configuration.
+// ensureSchedulingLayer guarantees that the scheduling subsystem is structurally complete.
+// It ensures a valid profile exists and injects missing architectural components (like Pickers and ProfileHandlers) if
+// they are not explicitly configured.
+func ensureSchedulingLayer(
+	cfg *configapi.EndpointPickerConfig,
+	handle plugins.Handle,
+	allPlugins map[string]plugins.Plugin,
+) error {
 	if len(cfg.SchedulingProfiles) == 0 {
-		cfg.SchedulingProfiles = make([]configapi.SchedulingProfile, 1)
-
-		thePlugins := []configapi.SchedulingPlugin{}
-		for pluginName, plugin := range allPlugins {
-			switch plugin.(type) {
+		defaultProfile := configapi.SchedulingProfile{Name: "default"}
+		// Auto-populate the default profile with all Filter, Scorer, and Picker plugins found.
+		for name, p := range allPlugins {
+			switch p.(type) {
 			case framework.Filter, framework.Scorer, framework.Picker:
-				thePlugins = append(thePlugins, configapi.SchedulingPlugin{PluginRef: pluginName})
+				defaultProfile.Plugins = append(defaultProfile.Plugins, configapi.SchedulingPlugin{PluginRef: name})
 			}
 		}
-
-		cfg.SchedulingProfiles[0] = configapi.SchedulingProfile{
-			Name:    "default",
-			Plugins: thePlugins,
-		}
+		cfg.SchedulingProfiles = []configapi.SchedulingProfile{defaultProfile}
 	}
 
-	// Add an instance of the SingleProfileHandler, if no profile handler was
-	// specified and the configuration has only one SchedulingProfile
+	// If there is only 1 profile and no handler is explicitly configured, use the SingleProfileHandler.
 	if len(cfg.SchedulingProfiles) == 1 {
-		profileHandlerFound := false
-		for _, plugin := range allPlugins {
-			if _, ok := plugin.(framework.ProfileHandler); ok {
-				profileHandlerFound = true
+		hasHandler := false
+		for _, p := range allPlugins {
+			if _, ok := p.(framework.ProfileHandler); ok {
+				hasHandler = true
 				break
 			}
 		}
-		if !profileHandlerFound {
-			handle.AddPlugin(profile.SingleProfileHandlerType, profile.NewSingleProfileHandler())
-			cfg.Plugins = append(cfg.Plugins,
-				configapi.PluginSpec{Name: profile.SingleProfileHandlerType,
-					Type: profile.SingleProfileHandlerType,
-				})
+		if !hasHandler {
+			if err := registerDefaultPlugin(cfg, handle, profile.SingleProfileHandlerType); err != nil {
+				return err
+			}
 		}
 	}
 
-	var maxScorePicker string
-	for pluginName, plugin := range allPlugins {
-		if _, ok := plugin.(framework.Picker); ok {
-			maxScorePicker = pluginName
+	// Find or Create a default MaxScorePicker to reuse across profiles.
+	var maxScorePickerName string
+	for name, p := range allPlugins {
+		if _, ok := p.(framework.Picker); ok {
+			maxScorePickerName = name
 			break
 		}
 	}
-	if maxScorePicker == "" {
-		handle.AddPlugin(picker.MaxScorePickerType, picker.NewMaxScorePicker(picker.DefaultMaxNumOfEndpoints))
-		maxScorePicker = picker.MaxScorePickerType
-		cfg.Plugins = append(cfg.Plugins, configapi.PluginSpec{Name: maxScorePicker, Type: maxScorePicker})
+
+	if maxScorePickerName == "" {
+		if err := registerDefaultPlugin(cfg, handle, picker.MaxScorePickerType); err != nil {
+			return err
+		}
+		maxScorePickerName = picker.MaxScorePickerType
 	}
 
-	for idx, theProfile := range cfg.SchedulingProfiles {
+	for i, prof := range cfg.SchedulingProfiles {
 		hasPicker := false
-		for pluginIdx, plugin := range theProfile.Plugins {
-			referencedPlugin := handle.Plugin(plugin.PluginRef)
-			if _, ok := referencedPlugin.(framework.Scorer); ok && plugin.Weight == nil {
-				theProfile.Plugins[pluginIdx].Weight = &defaultScorerWeight
-				cfg.SchedulingProfiles[idx] = theProfile
-			} else if _, ok := referencedPlugin.(framework.Picker); ok {
+		for j, pluginRef := range prof.Plugins {
+			p := handle.Plugin(pluginRef.PluginRef)
+
+			if _, ok := p.(framework.Scorer); ok && pluginRef.Weight == nil {
+				cfg.SchedulingProfiles[i].Plugins[j].Weight = &defaultScorerWeight
+			}
+
+			if _, ok := p.(framework.Picker); ok {
 				hasPicker = true
 			}
 		}
+
 		if !hasPicker {
-			theProfile.Plugins =
-				append(theProfile.Plugins, configapi.SchedulingPlugin{PluginRef: maxScorePicker})
-			cfg.SchedulingProfiles[idx] = theProfile
+			cfg.SchedulingProfiles[i].Plugins = append(
+				cfg.SchedulingProfiles[i].Plugins,
+				configapi.SchedulingPlugin{PluginRef: maxScorePickerName},
+			)
 		}
 	}
+
+	return nil
+}
+
+// registerDefaultPlugin instantiates a plugin with empty configuration (defaults) and adds it to both the handle and
+// the config spec.
+func registerDefaultPlugin(
+	cfg *configapi.EndpointPickerConfig,
+	handle plugins.Handle,
+	pluginType string,
+) error {
+	name := pluginType
+	factory, ok := plugins.Registry[pluginType]
+	if !ok {
+		return fmt.Errorf("plugin type '%s' not found in registry", pluginType)
+	}
+
+	plugin, err := factory(name, nil, handle)
+	if err != nil {
+		return fmt.Errorf("failed to instantiate default plugin '%s': %w", name, err)
+	}
+
+	handle.AddPlugin(name, plugin)
+	cfg.Plugins = append(cfg.Plugins, configapi.PluginSpec{
+		Name: name,
+		Type: pluginType,
+	})
+
+	return nil
 }
diff --git a/pkg/epp/config/loader/validation.go b/pkg/epp/config/loader/validation.go
index 7eed7ef..8df756d 100644
--- a/pkg/epp/config/loader/validation.go
+++ b/pkg/epp/config/loader/validation.go
@@ -17,53 +17,62 @@ limitations under the License.
 package loader
 
 import (
-	"errors"
 	"fmt"
 
 	"k8s.io/apimachinery/pkg/util/sets"
 	configapi "sigs.k8s.io/gateway-api-inference-extension/apix/config/v1alpha1"
 )
 
-func validateSchedulingProfiles(config *configapi.EndpointPickerConfig) error {
-	profileNames := sets.New[string]()
-	for _, profile := range config.SchedulingProfiles {
+// validateConfig performs a deep validation of the configuration integrity.
+// It checks relationships between profiles, plugins, and feature gates.
+func validateConfig(cfg *configapi.EndpointPickerConfig) error {
+	if err := validateFeatureGates(cfg.FeatureGates); err != nil {
+		return fmt.Errorf("feature gate validation failed: %w", err)
+	}
+	if err := validateSchedulingProfiles(cfg); err != nil {
+		return fmt.Errorf("scheduling profile validation failed: %w", err)
+	}
+	return nil
+}
+
+func validateSchedulingProfiles(cfg *configapi.EndpointPickerConfig) error {
+	definedPlugins := sets.New[string]()
+	for _, p := range cfg.Plugins {
+		definedPlugins.Insert(p.Name)
+	}
+	seenProfileNames := sets.New[string]()
+
+	for i, profile := range cfg.SchedulingProfiles {
 		if profile.Name == "" {
-			return errors.New("SchedulingProfile must have a name")
+			return fmt.Errorf("schedulingProfiles[%d] is missing a name", i)
 		}
-
-		if profileNames.Has(profile.Name) {
-			return fmt.Errorf("the name '%s' has been specified for more than one SchedulingProfile", profile.Name)
+		if seenProfileNames.Has(profile.Name) {
+			return fmt.Errorf("schedulingProfiles[%d] has duplicate name '%s'", i, profile.Name)
 		}
-		profileNames.Insert(profile.Name)
+		seenProfileNames.Insert(profile.Name)
 
-		for _, plugin := range profile.Plugins {
-			if len(plugin.PluginRef) == 0 {
-				return fmt.Errorf("SchedulingProfile '%s' plugins must have a plugin reference", profile.Name)
+		for j, pluginRef := range profile.Plugins {
+			if pluginRef.PluginRef == "" {
+				return fmt.Errorf("schedulingProfiles[%s].plugins[%d] is missing a 'pluginRef'", profile.Name, j)
 			}
 
-			notFound := true
-			for _, pluginConfig := range config.Plugins {
-				if plugin.PluginRef == pluginConfig.Name {
-					notFound = false
-					break
-				}
-			}
-			if notFound {
-				return errors.New(plugin.PluginRef + " is a reference to an undefined Plugin")
+			if !definedPlugins.Has(pluginRef.PluginRef) {
+				return fmt.Errorf("schedulingProfiles[%s] references undefined plugin '%s'",
+					profile.Name, pluginRef.PluginRef)
 			}
 		}
 	}
 	return nil
 }
 
-func validateFeatureGates(fg configapi.FeatureGates) error {
-	if fg == nil {
+func validateFeatureGates(gates configapi.FeatureGates) error {
+	if gates == nil {
 		return nil
 	}
 
-	for _, gate := range fg {
+	for _, gate := range gates {
 		if !registeredFeatureGates.Has(gate) {
-			return errors.New(gate + " is an unregistered Feature Gate")
+			return fmt.Errorf("feature gate '%s' is unknown or unregistered", gate)
 		}
 	}
 
diff --git a/pkg/epp/flowcontrol/contracts/mocks/mocks.go b/pkg/epp/flowcontrol/contracts/mocks/mocks.go
index 49205ee..c042f5a 100644
--- a/pkg/epp/flowcontrol/contracts/mocks/mocks.go
+++ b/pkg/epp/flowcontrol/contracts/mocks/mocks.go
@@ -41,6 +41,8 @@ import (
 	typesmocks "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/types/mocks"
 )
 
+// --- RegistryShard Mocks ---
+
 // MockRegistryShard is a simple "stub-style" mock for testing.
 // Its methods are implemented as function fields (e.g., `IDFunc`). A test can inject behavior by setting the desired
 // function field in the test setup. If a func is nil, the method will return a zero value.
@@ -111,6 +113,8 @@ func (m *MockRegistryShard) Stats() contracts.ShardStats {
 	return contracts.ShardStats{}
 }
 
+// --- Dependency Mocks ---
+
 // MockSaturationDetector is a simple "stub-style" mock for testing.
 type MockSaturationDetector struct {
 	IsSaturatedFunc func(ctx context.Context, candidatePods []metrics.PodMetrics) bool
@@ -123,6 +127,30 @@ func (m *MockSaturationDetector) IsSaturated(ctx context.Context, candidatePods
 	return false
 }
 
+// MockPodLocator provides a mock implementation of the contracts.PodLocator interface.
+// It allows tests to control the exact set of pods returned for a given request.
+type MockPodLocator struct {
+	// LocateFunc allows injecting custom logic.
+	LocateFunc func(ctx context.Context, requestMetadata map[string]any) []metrics.PodMetrics
+	// Pods is a static return value used if LocateFunc is nil.
+	Pods []metrics.PodMetrics
+}
+
+func (m *MockPodLocator) Locate(ctx context.Context, requestMetadata map[string]any) []metrics.PodMetrics {
+	if m.LocateFunc != nil {
+		return m.LocateFunc(ctx, requestMetadata)
+	}
+	// Return copy to be safe
+	if m.Pods == nil {
+		return nil
+	}
+	result := make([]metrics.PodMetrics, len(m.Pods))
+	copy(result, m.Pods)
+	return result
+}
+
+// --- ManagedQueue Mock ---
+
 // MockManagedQueue is a high-fidelity, thread-safe mock of the `contracts.ManagedQueue` interface, designed
 // specifically for testing the concurrent `controller/internal.ShardProcessor`.
 //
diff --git a/pkg/epp/flowcontrol/controller/controller.go b/pkg/epp/flowcontrol/controller/controller.go
index 751a420..29eda4e 100644
--- a/pkg/epp/flowcontrol/controller/controller.go
+++ b/pkg/epp/flowcontrol/controller/controller.go
@@ -60,6 +60,7 @@ type shardProcessorFactory func(
 	ctx context.Context,
 	shard contracts.RegistryShard,
 	saturationDetector contracts.SaturationDetector,
+	podLocator contracts.PodLocator,
 	clock clock.WithTicker,
 	cleanupSweepInterval time.Duration,
 	enqueueChannelBufferSize int,
@@ -95,6 +96,7 @@ type FlowController struct {
 	config                Config
 	registry              registryClient
 	saturationDetector    contracts.SaturationDetector
+	podLocator            contracts.PodLocator
 	clock                 clock.WithTicker
 	logger                logr.Logger
 	shardProcessorFactory shardProcessorFactory
@@ -126,6 +128,7 @@ func NewFlowController(
 	config Config,
 	registry contracts.FlowRegistry,
 	sd contracts.SaturationDetector,
+	podLocator contracts.PodLocator,
 	logger logr.Logger,
 	opts ...flowControllerOption,
 ) (*FlowController, error) {
@@ -133,6 +136,7 @@ func NewFlowController(
 		config:             config,
 		registry:           registry,
 		saturationDetector: sd,
+		podLocator:         podLocator,
 		clock:              clock.RealClock{},
 		logger:             logger.WithName("flow-controller"),
 		parentCtx:          ctx,
@@ -142,6 +146,7 @@ func NewFlowController(
 		ctx context.Context,
 		shard contracts.RegistryShard,
 		saturationDetector contracts.SaturationDetector,
+		podLocator contracts.PodLocator,
 		clock clock.WithTicker,
 		cleanupSweepInterval time.Duration,
 		enqueueChannelBufferSize int,
@@ -151,6 +156,7 @@ func NewFlowController(
 			ctx,
 			shard,
 			saturationDetector,
+			podLocator,
 			clock,
 			cleanupSweepInterval,
 			enqueueChannelBufferSize,
@@ -448,6 +454,7 @@ func (fc *FlowController) getOrStartWorker(shard contracts.RegistryShard) *manag
 		processorCtx,
 		shard,
 		fc.saturationDetector,
+		fc.podLocator,
 		fc.clock,
 		fc.config.ExpiryCleanupInterval,
 		fc.config.EnqueueChannelBufferSize,
diff --git a/pkg/epp/flowcontrol/controller/internal/processor.go b/pkg/epp/flowcontrol/controller/internal/processor.go
index 489f421..ae419af 100644
--- a/pkg/epp/flowcontrol/controller/internal/processor.go
+++ b/pkg/epp/flowcontrol/controller/internal/processor.go
@@ -65,6 +65,7 @@ var ErrProcessorBusy = errors.New("shard processor is busy")
 type ShardProcessor struct {
 	shard                contracts.RegistryShard
 	saturationDetector   contracts.SaturationDetector
+	podLocator           contracts.PodLocator
 	clock                clock.WithTicker
 	cleanupSweepInterval time.Duration
 	logger               logr.Logger
@@ -86,6 +87,7 @@ func NewShardProcessor(
 	ctx context.Context,
 	shard contracts.RegistryShard,
 	saturationDetector contracts.SaturationDetector,
+	podLocator contracts.PodLocator,
 	clock clock.WithTicker,
 	cleanupSweepInterval time.Duration,
 	enqueueChannelBufferSize int,
@@ -94,6 +96,7 @@ func NewShardProcessor(
 	return &ShardProcessor{
 		shard:                shard,
 		saturationDetector:   saturationDetector,
+		podLocator:           podLocator,
 		clock:                clock,
 		cleanupSweepInterval: cleanupSweepInterval,
 		logger:               logger,
@@ -307,8 +310,8 @@ func (sp *ShardProcessor) dispatchCycle(ctx context.Context) bool {
 
 		// --- Viability Check (Saturation/HoL Blocking) ---
 		req := item.OriginalRequest()
-		candidatePods := req.CandidatePodsForScheduling()
-		if sp.saturationDetector.IsSaturated(ctx, candidatePods) {
+		candidates := sp.podLocator.Locate(ctx, req.GetMetadata())
+		if sp.saturationDetector.IsSaturated(ctx, candidates) {
 			sp.logger.V(logutil.DEBUG).Info("Policy's chosen item is saturated; enforcing HoL blocking.",
 				"flowKey", req.FlowKey(), "reqID", req.ID(), "priorityName", originalBand.PriorityName())
 			// Stop the dispatch cycle entirely to respect strict policy decision and prevent priority inversion where
diff --git a/pkg/epp/flowcontrol/types/mocks/mocks.go b/pkg/epp/flowcontrol/types/mocks/mocks.go
index 5fabf36..2e12b04 100644
--- a/pkg/epp/flowcontrol/types/mocks/mocks.go
+++ b/pkg/epp/flowcontrol/types/mocks/mocks.go
@@ -21,20 +21,19 @@ package mocks
 import (
 	"time"
 
-	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/types"
 )
 
-// MockFlowControlRequest provides a mock implementation of the `types.FlowControlRequest` interface.
+// MockFlowControlRequest provides a mock implementation of the types.FlowControlRequest interface.
 type MockFlowControlRequest struct {
-	FlowKeyV                    types.FlowKey
-	ByteSizeV                   uint64
-	InitialEffectiveTTLV        time.Duration
-	IDV                         string
-	CandidatePodsForSchedulingV []*metrics.FakePodMetrics
+	FlowKeyV             types.FlowKey
+	ByteSizeV            uint64
+	InitialEffectiveTTLV time.Duration
+	IDV                  string
+	MetadataV            map[string]any
 }
 
-// NewMockFlowControlRequest creates a new `MockFlowControlRequest` instance.
+// NewMockFlowControlRequest creates a new MockFlowControlRequest instance.
 func NewMockFlowControlRequest(
 	byteSize uint64,
 	id string,
@@ -44,6 +43,7 @@ func NewMockFlowControlRequest(
 		ByteSizeV: byteSize,
 		IDV:       id,
 		FlowKeyV:  key,
+		MetadataV: make(map[string]any),
 	}
 }
 
@@ -51,14 +51,7 @@ func (m *MockFlowControlRequest) FlowKey() types.FlowKey             { return m.
 func (m *MockFlowControlRequest) ByteSize() uint64                   { return m.ByteSizeV }
 func (m *MockFlowControlRequest) InitialEffectiveTTL() time.Duration { return m.InitialEffectiveTTLV }
 func (m *MockFlowControlRequest) ID() string                         { return m.IDV }
-
-func (m *MockFlowControlRequest) CandidatePodsForScheduling() []metrics.PodMetrics {
-	pods := make([]metrics.PodMetrics, 0, len(m.CandidatePodsForSchedulingV))
-	for i, pod := range m.CandidatePodsForSchedulingV {
-		pods[i] = pod
-	}
-	return pods
-}
+func (m *MockFlowControlRequest) GetMetadata() map[string]any        { return m.MetadataV }
 
 var _ types.FlowControlRequest = &MockFlowControlRequest{}
 
diff --git a/pkg/epp/flowcontrol/types/request.go b/pkg/epp/flowcontrol/types/request.go
index e427b0a..61ac320 100644
--- a/pkg/epp/flowcontrol/types/request.go
+++ b/pkg/epp/flowcontrol/types/request.go
@@ -18,8 +18,6 @@ package types
 
 import (
 	"time"
-
-	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
 )
 
 // FlowControlRequest is the contract for an incoming request submitted to the `controller.FlowController`. It
@@ -45,15 +43,15 @@ type FlowControlRequest interface {
 	// applied.
 	InitialEffectiveTTL() time.Duration
 
-	// CandidatePodsForScheduling passes through a set of candidate pods a request may be admitted to.
-	// This is necessary for invoking `contracts.SaturationDetector.IsSaturated`, but it is otherwise unused in the Flow
-	// Control system.
-	CandidatePodsForScheduling() []metrics.PodMetrics
-
 	// ID returns an optional, user-facing unique identifier for this specific request. It is intended for logging,
 	// tracing, and observability. The `controller.FlowController` does not use this ID for dispatching decisions; it uses
 	// the internal, opaque `QueueItemHandle`.
 	ID() string
+
+	// GetMetadata returns the opaque metadata associated with the request (e.g., header-derived context, subset filters).
+	// This data is passed transparently to components like the contracts.PodLocator to resolve resources (candidate pods)
+	// lazily during the dispatch cycle.
+	GetMetadata() map[string]any
 }
 
 // QueueItemHandle is an opaque handle to an item that has been successfully added to a `framework.SafeQueue`. It acts
diff --git a/pkg/epp/requestcontrol/admission.go b/pkg/epp/requestcontrol/admission.go
index 69fd5ad..2a806b5 100644
--- a/pkg/epp/requestcontrol/admission.go
+++ b/pkg/epp/requestcontrol/admission.go
@@ -20,9 +20,11 @@ import (
 	"context"
 	"time"
 
+	"github.com/go-logr/logr"
 	"sigs.k8s.io/controller-runtime/pkg/log"
 
 	backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/contracts"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/types"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/handlers"
 	errutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/error"
@@ -40,7 +42,6 @@ type AdmissionController interface {
 	// Args:
 	//   ctx: The request context, carrying deadlines, cancellation signals, and logger.
 	//   reqCtx: The handlers.RequestContext containing details about the incoming request.
-	//   candidatePods: A list of potential backend pods that can serve the request.
 	//   priority: The priority level of the request, as determined by the InferenceObjective.
 	//
 	// Returns:
@@ -49,7 +50,6 @@ type AdmissionController interface {
 	Admit(
 		ctx context.Context,
 		reqCtx *handlers.RequestContext,
-		candidatePods []backendmetrics.PodMetrics,
 		priority int,
 	) error
 }
@@ -65,18 +65,17 @@ type flowController interface {
 	EnqueueAndWait(ctx context.Context, req types.FlowControlRequest) (types.QueueOutcome, error)
 }
 
-// rejectIfSheddableAndSaturated checks if a request should be immediately rejected because it's sheddable
-// (priority < 0) and the system is saturated.
+// rejectIfSheddableAndSaturated checks if a request should be immediately rejected.
 func rejectIfSheddableAndSaturated(
 	ctx context.Context,
 	sd saturationDetector,
+	locator contracts.PodLocator,
 	reqCtx *handlers.RequestContext,
-	candidatePods []backendmetrics.PodMetrics,
 	priority int,
+	logger logr.Logger,
 ) error {
 	if requtil.IsSheddable(priority) {
-		logger := log.FromContext(ctx)
-		if sd.IsSaturated(ctx, candidatePods) {
+		if sd.IsSaturated(ctx, locator.Locate(ctx, reqCtx.Request.Metadata)) {
 			logger.V(logutil.TRACE).Info("Request rejected: system saturated and request is sheddable",
 				"requestID", reqCtx.SchedulingRequest.RequestId)
 			return errutil.Error{
@@ -95,11 +94,18 @@ func rejectIfSheddableAndSaturated(
 // saturated. Non-sheddable requests always bypass the saturation check.
 type LegacyAdmissionController struct {
 	saturationDetector saturationDetector
+	podLocator         contracts.PodLocator
 }
 
 // NewLegacyAdmissionController creates a new LegacyAdmissionController.
-func NewLegacyAdmissionController(sd saturationDetector) *LegacyAdmissionController {
-	return &LegacyAdmissionController{saturationDetector: sd}
+func NewLegacyAdmissionController(
+	sd saturationDetector,
+	pl contracts.PodLocator,
+) *LegacyAdmissionController {
+	return &LegacyAdmissionController{
+		saturationDetector: sd,
+		podLocator:         pl,
+	}
 }
 
 // Admit implements the AdmissionController interface for the legacy strategy.
@@ -107,13 +113,18 @@ func NewLegacyAdmissionController(sd saturationDetector) *LegacyAdmissionControl
 func (lac *LegacyAdmissionController) Admit(
 	ctx context.Context,
 	reqCtx *handlers.RequestContext,
-	candidatePods []backendmetrics.PodMetrics,
 	priority int,
 ) error {
 	logger := log.FromContext(ctx)
 	logger.V(logutil.TRACE).Info("Executing LegacyAdmissionController",
 		"priority", priority, "fairnessID", reqCtx.FairnessID)
-	if err := rejectIfSheddableAndSaturated(ctx, lac.saturationDetector, reqCtx, candidatePods, priority); err != nil {
+	if err := rejectIfSheddableAndSaturated(
+		ctx,
+		lac.saturationDetector,
+		lac.podLocator,
+		reqCtx, priority,
+		logger,
+	); err != nil {
 		return err
 	}
 	logger.V(logutil.TRACE).Info("Request admitted", "requestID", reqCtx.SchedulingRequest.RequestId)
@@ -123,19 +134,15 @@ func (lac *LegacyAdmissionController) Admit(
 // --- FlowControlAdmissionController ---
 
 // FlowControlAdmissionController delegates admission decisions to the Flow Control layer.
-// It first checks if the request is sheddable and the system is saturated, rejecting immediately if both conditions are
-// true. Otherwise, it uses the provided flowController to enqueue the request and await an outcome.
+// It uses the provided Flow Controller to enqueue the request and await an outcome.
 type FlowControlAdmissionController struct {
-	saturationDetector saturationDetector
-	flowController     flowController
+	flowController flowController
 }
 
 // NewFlowControlAdmissionController creates a new FlowControlAdmissionController.
-// It requires a SaturationDetector and a flowController instance.
-func NewFlowControlAdmissionController(sd saturationDetector, fc flowController) *FlowControlAdmissionController {
+func NewFlowControlAdmissionController(fc flowController) *FlowControlAdmissionController {
 	return &FlowControlAdmissionController{
-		saturationDetector: sd,
-		flowController:     fc,
+		flowController: fc,
 	}
 }
 
@@ -144,24 +151,18 @@ func NewFlowControlAdmissionController(sd saturationDetector, fc flowController)
 func (fcac *FlowControlAdmissionController) Admit(
 	ctx context.Context,
 	reqCtx *handlers.RequestContext,
-	candidatePods []backendmetrics.PodMetrics,
 	priority int,
 ) error {
 	logger := log.FromContext(ctx)
 	logger.V(logutil.TRACE).Info("Executing FlowControlAdmissionController",
 		"requestID", reqCtx.SchedulingRequest.RequestId, "priority", priority, "fairnessID", reqCtx.FairnessID)
-	if err := rejectIfSheddableAndSaturated(ctx, fcac.saturationDetector, reqCtx, candidatePods, priority); err != nil {
-		return err
-	}
-
-	logger.V(logutil.TRACE).Info("Request proceeding to flow control", "requestID", reqCtx.SchedulingRequest.RequestId)
 
 	fcReq := &flowControlRequest{
 		requestID:       reqCtx.SchedulingRequest.RequestId,
 		fairnessID:      reqCtx.FairnessID,
 		priority:        priority,
 		requestByteSize: uint64(reqCtx.RequestSize),
-		candidatePods:   candidatePods,
+		reqMetadata:     reqCtx.Request.Metadata,
 	}
 
 	outcome, err := fcac.flowController.EnqueueAndWait(ctx, fcReq)
@@ -176,7 +177,7 @@ type flowControlRequest struct {
 	fairnessID      string
 	priority        int
 	requestByteSize uint64
-	candidatePods   []backendmetrics.PodMetrics
+	reqMetadata     map[string]any
 }
 
 var _ types.FlowControlRequest = &flowControlRequest{}
@@ -184,12 +185,12 @@ var _ types.FlowControlRequest = &flowControlRequest{}
 func (r *flowControlRequest) ID() string                         { return r.requestID }
 func (r *flowControlRequest) InitialEffectiveTTL() time.Duration { return 0 } // Use controller default.
 func (r *flowControlRequest) ByteSize() uint64                   { return r.requestByteSize }
-func (r *flowControlRequest) CandidatePodsForScheduling() []backendmetrics.PodMetrics {
-	return r.candidatePods
-}
 func (r *flowControlRequest) FlowKey() types.FlowKey {
 	return types.FlowKey{ID: r.fairnessID, Priority: r.priority}
 }
+func (r *flowControlRequest) GetMetadata() map[string]any {
+	return r.reqMetadata
+}
 
 // translateFlowControlOutcome maps the context-rich outcome of the Flow Control layer to the public errutil.Error
 // contract used by the Director.
diff --git a/pkg/epp/requestcontrol/director.go b/pkg/epp/requestcontrol/director.go
index 19e69cf..e4af1c4 100644
--- a/pkg/epp/requestcontrol/director.go
+++ b/pkg/epp/requestcontrol/director.go
@@ -159,15 +159,17 @@ func (d *Director) HandleRequest(ctx context.Context, reqCtx *handlers.RequestCo
 	ctx = log.IntoContext(ctx, logger)
 	logger.V(logutil.DEBUG).Info("LLM request assembled")
 
-	// Get candidate pods for scheduling
-	candidatePods := d.podLocator.Locate(ctx, reqCtx.Request.Metadata)
-	if len(candidatePods) == 0 {
-		return reqCtx, errutil.Error{Code: errutil.ServiceUnavailable, Msg: "failed to find candidate pods for serving the request"}
-	}
-	if err := d.admissionController.Admit(ctx, reqCtx, candidatePods, *infObjective.Spec.Priority); err != nil {
+	if err := d.admissionController.Admit(ctx, reqCtx, *infObjective.Spec.Priority); err != nil {
 		logger.V(logutil.DEFAULT).Info("Request rejected by admission control", "error", err)
 		return reqCtx, err
 	}
+	candidatePods := d.podLocator.Locate(ctx, reqCtx.Request.Metadata)
+	if len(candidatePods) == 0 {
+		return reqCtx, errutil.Error{
+			Code: errutil.ServiceUnavailable,
+			Msg:  "failed to find candidate pods for serving the request",
+		}
+	}
 	snapshotOfCandidatePods := d.toSchedulerPodMetrics(candidatePods)
 
 	// Prepare per request data by running PrepareData plugins.
diff --git a/site-src/_includes/prereqs.md b/site-src/_includes/prereqs.md
index 0c655a1..2be8f50 100644
--- a/site-src/_includes/prereqs.md
+++ b/site-src/_includes/prereqs.md
@@ -6,6 +6,7 @@ A cluster with:
 - Support for [sidecar containers](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/) (enabled by default since Kubernetes v1.29)
   to run the model server deployment.
 
-Tooling:
+Tools:
 
-- [Helm](https://helm.sh/docs/intro/install/) installed.
+- [Helm](https://helm.sh/docs/intro/install/).
+- [jq](https://jqlang.org/download/).
diff --git a/site-src/guides/index.md b/site-src/guides/index.md
index c046168..80d65ea 100644
--- a/site-src/guides/index.md
+++ b/site-src/guides/index.md
@@ -10,6 +10,15 @@
 
 ## **Steps**
 
+### Set Latest Release Variable
+
+```bash
+IGW_LATEST_RELEASE=$(curl -s https://api.github.com/repos/kubernetes-sigs/gateway-api-inference-extension/releases \
+  | jq -r '.[] | select(.prerelease == false) | .tag_name' \
+  | sort -V \
+  | tail -n1)
+```
+
 ### Deploy Sample Model Server
 
 --8<-- "site-src/_includes/model-server-intro.md"
@@ -18,25 +27,25 @@
 
     ```bash
     kubectl create secret generic hf-token --from-literal=token=$HF_TOKEN # Your Hugging Face Token with access to the set of Llama models
-    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/gpu-deployment.yaml
+    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/gpu-deployment.yaml
     ```
 
 --8<-- "site-src/_includes/model-server-cpu.md"
 
     ```bash
-    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/cpu-deployment.yaml
+    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/cpu-deployment.yaml
     ```
 
 --8<-- "site-src/_includes/model-server-sim.md"
 
     ```bash
-    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/sim-deployment.yaml
+    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/sim-deployment.yaml
     ```
 
 ### Install the Inference Extension CRDs
 
 ```bash
-kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.2.1/manifests.yaml
+kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/${IGW_LATEST_RELEASE}/manifests.yaml
 ```
 
 ### Install the Gateway
@@ -115,7 +124,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
    Set the chart version and then select a tab to follow the provider-specific instructions.
 
    ```bash
-   export IGW_CHART_VERSION=v1.2.1
+   export IGW_CHART_VERSION=${IGW_LATEST_RELEASE}
    ```
 
 --8<-- "site-src/_includes/epp.md"
@@ -133,7 +142,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the Inference Gateway:
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/gateway.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/gateway.yaml
          ```
 
          Confirm that the Gateway was assigned an IP address and reports a `Programmed=True` status:
@@ -146,7 +155,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the HTTPRoute:
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/httproute.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/httproute.yaml
          ```
 
       1. Confirm that the HTTPRoute status conditions include `Accepted=True` and `ResolvedRefs=True`:
@@ -163,7 +172,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the Inference Gateway:
 
          ```bash
-         kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/main/config/manifests/gateway/istio/gateway.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/istio/gateway.yaml
          ```
 
          Confirm that the Gateway was assigned an IP address and reports a `Programmed=True` status:
@@ -176,7 +185,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the HTTPRoute:
 
          ```bash
-         kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/main/config/manifests/gateway/istio/httproute.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/istio/httproute.yaml
          ```
 
       1. Confirm that the HTTPRoute status conditions include `Accepted=True` and `ResolvedRefs=True`:
@@ -195,7 +204,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the Inference Gateway:
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/agentgateway/gateway.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/agentgateway/gateway.yaml
          ```
 
          Confirm that the Gateway was assigned an IP address and reports a `Programmed=True` status:
@@ -206,7 +215,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the HTTPRoute:
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/agentgateway/httproute.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/agentgateway/httproute.yaml
          ```
 
       1. Confirm that the HTTPRoute status conditions include `Accepted=True` and `ResolvedRefs=True`:
@@ -222,7 +231,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
       1. Deploy the Gateway
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.0.2/config/manifests/gateway/nginxgatewayfabric/gateway.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/nginxgatewayfabric/gateway.yaml
          ```
 
       2. Verify the Gateway status
@@ -240,7 +249,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
          Create the HTTPRoute resource to route traffic to your InferencePool:
 
          ```bash
-         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.0.2/config/manifests/gateway/nginxgatewayfabric/httproute.yaml
+         kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/nginxgatewayfabric/httproute.yaml
          ```
 
       4. Verify the route status
@@ -271,7 +280,7 @@ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extens
 Deploy the sample InferenceObjective which allows you to specify priority of requests.
 
    ```bash
-   kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/inferenceobjective.yaml
+   kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/inferenceobjective.yaml
    ```
 
 --8<-- "site-src/_includes/test.md"
@@ -293,17 +302,17 @@ You have now deployed a basic Inference Gateway with a simple routing strategy.
 
       ```bash
       helm uninstall vllm-llama3-8b-instruct
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/inferenceobjective.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/cpu-deployment.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/gpu-deployment.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/vllm/sim-deployment.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/inferenceobjective.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/cpu-deployment.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/gpu-deployment.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/vllm/sim-deployment.yaml --ignore-not-found
       kubectl delete secret hf-token --ignore-not-found
       ```
 
    1. Uninstall the Gateway API Inference Extension CRDs:
 
       ```bash
-      kubectl delete -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.2.1/manifests.yaml --ignore-not-found
+      kubectl delete -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/${IGW_LATEST_RELEASE}/manifests.yaml --ignore-not-found
       ```
       
    1. Choose one of the following options to cleanup the Inference Gateway.
@@ -311,17 +320,17 @@ You have now deployed a basic Inference Gateway with a simple routing strategy.
 === "GKE"
 
       ```bash
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/gateway.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/healthcheck.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/gcp-backend-policy.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/gke/httproute.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/gateway.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/healthcheck.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/gcp-backend-policy.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/gke/httproute.yaml --ignore-not-found
       ```
 
 === "Istio"
 
       ```bash
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/istio/gateway.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/istio/httproute.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/istio/gateway.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/istio/httproute.yaml --ignore-not-found
       ```
 
       The following steps assume you would like to clean up ALL Istio resources that were created in this quickstart guide.
@@ -341,8 +350,8 @@ You have now deployed a basic Inference Gateway with a simple routing strategy.
 === "Kgateway"
 
       ```bash
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/agentgateway/gateway.yaml --ignore-not-found
-      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.2.1/config/manifests/gateway/agentgateway/httproute.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/agentgateway/gateway.yaml --ignore-not-found
+      kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/agentgateway/httproute.yaml --ignore-not-found
       ```
 
       The following steps assume you would like to cleanup ALL Kgateway resources that were created in this quickstart guide.
@@ -373,8 +382,8 @@ You have now deployed a basic Inference Gateway with a simple routing strategy.
       1. Remove Inference Gateway and HTTPRoute:
 
          ```bash
-         kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.0.2/config/manifests/gateway/nginxgatewayfabric/gateway.yaml --ignore-not-found
-         kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/v1.0.2/config/manifests/gateway/nginxgatewayfabric/httproute.yaml --ignore-not-found
+         kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/nginxgatewayfabric/gateway.yaml --ignore-not-found
+         kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/refs/tags/${IGW_LATEST_RELEASE}/config/manifests/gateway/nginxgatewayfabric/httproute.yaml --ignore-not-found
          ```
 
       2. Uninstall NGINX Gateway Fabric:



Test patch:
diff --git a/pkg/epp/config/loader/configloader_test.go b/pkg/epp/config/loader/configloader_test.go
index f925e71..82f57f4 100644
--- a/pkg/epp/config/loader/configloader_test.go
+++ b/pkg/epp/config/loader/configloader_test.go
@@ -19,21 +19,20 @@ package loader
 import (
 	"context"
 	"encoding/json"
-	"os"
 	"testing"
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/stretchr/testify/require"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/utils/ptr"
 
 	configapi "sigs.k8s.io/gateway-api-inference-extension/apix/config/v1alpha1"
-	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/config"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/saturationdetector"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework"
-	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/multi/prefix"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/picker"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/profile"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/types"
@@ -41,1024 +40,427 @@ import (
 	"sigs.k8s.io/gateway-api-inference-extension/test/utils"
 )
 
+// Define constants for test plugins.
+// Constants must match those used in testdata_test.go.
 const (
-	testProfileHandlerType = "test-profile-handler"
-	test1Type              = "test-one"
-	test2Type              = "test-two"
-	testPickerType         = "test-picker"
+	testPluginType     = "test-plugin"
+	testPickerType     = "test-picker"
+	testScorerType     = "test-scorer"
+	testProfileHandler = "test-profile-handler"
 )
 
-type testStruct struct {
-	name       string
-	configText string
-	configFile string
-	want       *configapi.EndpointPickerConfig
-	wantErr    bool
-}
+// --- Test: Phase 1 (Raw Loading & Static Defaults) ---
 
 func TestLoadRawConfiguration(t *testing.T) {
-	registerTestPlugins()
+	t.Parallel()
 
-	goodConfig := &configapi.EndpointPickerConfig{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "EndpointPickerConfig",
-			APIVersion: "inference.networking.x-k8s.io/v1alpha1",
-		},
-		Plugins: []configapi.PluginSpec{
-			{
-				Name:       "test1",
-				Type:       test1Type,
-				Parameters: json.RawMessage("{\"threshold\":10}"),
-			},
-			{
-				Name: "profileHandler",
-				Type: "test-profile-handler",
-			},
-			{
-				Type:       test2Type,
-				Parameters: json.RawMessage("{\"blockSize\":32}"),
-			},
-			{
-				Name: "testPicker",
-				Type: testPickerType,
-			},
-		},
-		SchedulingProfiles: []configapi.SchedulingProfile{
-			{
-				Name: "default",
-				Plugins: []configapi.SchedulingPlugin{
-					{
-						PluginRef: "test1",
-					},
-					{
-						PluginRef: "test-two",
-						Weight:    ptr.To(50),
-					},
-					{
-						PluginRef: "testPicker",
-					},
-				},
-			},
-		},
-		FeatureGates: configapi.FeatureGates{datalayer.FeatureGate},
-		SaturationDetector: &configapi.SaturationDetector{
-			MetricsStalenessThreshold: metav1.Duration{Duration: 150 * time.Millisecond},
-		},
-	}
-
-	goodConfigNoProfiles := &configapi.EndpointPickerConfig{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "EndpointPickerConfig",
-			APIVersion: "inference.networking.x-k8s.io/v1alpha1",
-		},
-		Plugins: []configapi.PluginSpec{
-			{
-				Name:       "test1",
-				Type:       test1Type,
-				Parameters: json.RawMessage("{\"threshold\":10}"),
-			},
-		},
-	}
+	// Register known feature gates for validation.
+	RegisterFeatureGate(datalayer.FeatureGate)
 
-	tests := []testStruct{
+	tests := []struct {
+		name       string
+		configText string
+		want       *configapi.EndpointPickerConfig
+		wantErr    bool
+	}{
 		{
-			name:       "success",
+			name:       "Success - Full Configuration",
 			configText: successConfigText,
-			configFile: "",
-			want:       goodConfig,
-			wantErr:    false,
-		},
-		{
-			name:       "successNoProfiles",
-			configText: successNoProfilesText,
-			configFile: "",
-			want:       goodConfigNoProfiles,
-			wantErr:    false,
-		},
-		{
-			name:       "errorBadYaml",
-			configText: errorBadYamlText,
-			configFile: "",
-			wantErr:    true,
-		},
-		{
-			name:       "successFromFile",
-			configText: "",
-			configFile: "../../../../test/testdata/configloader_1_test.yaml",
-			want:       goodConfig,
-			wantErr:    false,
-		},
-	}
-
-	for _, test := range tests {
-		configBytes := []byte(test.configText)
-		if test.configFile != "" {
-			configBytes, _ = os.ReadFile(test.configFile)
-		}
-
-		got, err := loadRawConfig(configBytes)
-		checker(t, "loadRawConfig", test, got, err)
-	}
-}
-
-func TestLoadRawConfigurationWithDefaults(t *testing.T) {
-	registerTestPlugins()
-
-	goodConfig := &configapi.EndpointPickerConfig{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "EndpointPickerConfig",
-			APIVersion: "inference.networking.x-k8s.io/v1alpha1",
-		},
-		Plugins: []configapi.PluginSpec{
-			{
-				Name:       "test1",
-				Type:       test1Type,
-				Parameters: json.RawMessage("{\"threshold\":10}"),
-			},
-			{
-				Name: "profileHandler",
-				Type: "test-profile-handler",
-			},
-			{
-				Name:       test2Type,
-				Type:       test2Type,
-				Parameters: json.RawMessage("{\"blockSize\":32}"),
-			},
-			{
-				Name: "testPicker",
-				Type: testPickerType,
-			},
-		},
-		SchedulingProfiles: []configapi.SchedulingProfile{
-			{
-				Name: "default",
-				Plugins: []configapi.SchedulingPlugin{
-					{
-						PluginRef: "test1",
-					},
-					{
-						PluginRef: "test-two",
-						Weight:    ptr.To(50),
-					},
-					{
-						PluginRef: "testPicker",
-					},
+			want: &configapi.EndpointPickerConfig{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "EndpointPickerConfig",
+					APIVersion: "inference.networking.x-k8s.io/v1alpha1",
 				},
-			},
-		},
-		FeatureGates: configapi.FeatureGates{datalayer.FeatureGate},
-		SaturationDetector: &configapi.SaturationDetector{
-			QueueDepthThreshold:       saturationdetector.DefaultQueueDepthThreshold,
-			KVCacheUtilThreshold:      saturationdetector.DefaultKVCacheUtilThreshold,
-			MetricsStalenessThreshold: metav1.Duration{Duration: 150 * time.Millisecond},
-		},
-	}
-
-	goodConfigNoProfiles := &configapi.EndpointPickerConfig{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "EndpointPickerConfig",
-			APIVersion: "inference.networking.x-k8s.io/v1alpha1",
-		},
-		Plugins: []configapi.PluginSpec{
-			{
-				Name:       "test1",
-				Type:       test1Type,
-				Parameters: json.RawMessage("{\"threshold\":10}"),
-			},
-			{
-				Name: profile.SingleProfileHandlerType,
-				Type: profile.SingleProfileHandlerType,
-			},
-			{
-				Name: picker.MaxScorePickerType,
-				Type: picker.MaxScorePickerType,
-			},
-		},
-		SchedulingProfiles: []configapi.SchedulingProfile{
-			{
-				Name: "default",
-				Plugins: []configapi.SchedulingPlugin{
-					{
-						PluginRef: "test1",
-					},
+				Plugins: []configapi.PluginSpec{
+					{Name: "test1", Type: testPluginType, Parameters: json.RawMessage(`{"threshold":10}`)},
+					{Name: "profileHandler", Type: testProfileHandler},
+					{Name: testScorerType, Type: testScorerType, Parameters: json.RawMessage(`{"blockSize":32}`)},
+					{Name: "testPicker", Type: testPickerType},
+				},
+				SchedulingProfiles: []configapi.SchedulingProfile{
 					{
-						PluginRef: "max-score-picker",
+						Name: "default",
+						Plugins: []configapi.SchedulingPlugin{
+							{PluginRef: "test1"},
+							{PluginRef: testScorerType, Weight: ptr.To(50)},
+							{PluginRef: "testPicker"},
+						},
 					},
 				},
+				FeatureGates: configapi.FeatureGates{datalayer.FeatureGate},
+				SaturationDetector: &configapi.SaturationDetector{
+					QueueDepthThreshold:       10,
+					KVCacheUtilThreshold:      0.8,
+					MetricsStalenessThreshold: metav1.Duration{Duration: 100 * time.Millisecond},
+				},
 			},
-		},
-		FeatureGates: configapi.FeatureGates{},
-		SaturationDetector: &configapi.SaturationDetector{
-			QueueDepthThreshold:       saturationdetector.DefaultQueueDepthThreshold,
-			KVCacheUtilThreshold:      saturationdetector.DefaultKVCacheUtilThreshold,
-			MetricsStalenessThreshold: metav1.Duration{Duration: saturationdetector.DefaultMetricsStalenessThreshold},
-		},
-	}
-
-	tests := []testStruct{
-		{
-			name:       "success",
-			configText: successConfigText,
-			configFile: "",
-			want:       goodConfig,
-			wantErr:    false,
+			wantErr: false,
 		},
 		{
-			name:       "successNoProfiles",
+			name:       "Success - No Profiles",
 			configText: successNoProfilesText,
-			configFile: "",
-			want:       goodConfigNoProfiles,
-			wantErr:    false,
-		},
-		{
-			name:       "errorBadPluginReferenceText",
-			configText: errorBadPluginReferenceText,
-			configFile: "",
-			wantErr:    true,
-		},
-		{
-			name:       "errorBadPluginReferencePluginText",
-			configText: errorBadPluginReferencePluginText,
-			configFile: "",
-			wantErr:    true,
-		},
-		{
-			name:       "errorNoProfileName",
-			configText: errorNoProfileNameText,
-			configFile: "",
-			wantErr:    true,
-		},
-		{
-			name:       "errorBadProfilePlugin",
-			configText: errorBadProfilePluginText,
-			configFile: "",
-			wantErr:    true,
-		},
-		{
-			name:       "errorBadProfilePluginRef",
-			configText: errorBadProfilePluginRefText,
-			configFile: "",
-			wantErr:    true,
+			want: &configapi.EndpointPickerConfig{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "EndpointPickerConfig",
+					APIVersion: "inference.networking.x-k8s.io/v1alpha1",
+				},
+				Plugins: []configapi.PluginSpec{
+					{Name: "test1", Type: testPluginType, Parameters: json.RawMessage(`{"threshold":10}`)},
+				},
+				FeatureGates: configapi.FeatureGates{},
+			},
+			wantErr: false,
 		},
 		{
-			name:       "errorDuplicatePlugin",
-			configText: errorDuplicatePluginText,
-			configFile: "",
+			name:       "Error - Invalid YAML",
+			configText: errorBadYamlText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorDuplicateProfile",
-			configText: errorDuplicateProfileText,
-			configFile: "",
+			name:       "Error - Unknown Feature Gate",
+			configText: errorUnknownFeatureGateText,
 			wantErr:    true,
 		},
-		{
-			name:       "successFromFile",
-			configText: "",
-			configFile: "../../../../test/testdata/configloader_1_test.yaml",
-			want:       goodConfig,
-			wantErr:    false,
-		},
 	}
 
-	for _, test := range tests {
-		handle := utils.NewTestHandle(context.Background())
-
-		configBytes := []byte(test.configText)
-		if test.configFile != "" {
-			configBytes, _ = os.ReadFile(test.configFile)
-		}
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			logger := logging.NewTestLogger()
 
-		got, err := loadRawConfig(configBytes)
-		if err == nil {
-			setDefaultsPhaseOne(got)
+			got, _, err := LoadRawConfig([]byte(tc.configText), logger)
 
-			err = instantiatePlugins(got.Plugins, handle)
-			if err == nil {
-				setDefaultsPhaseTwo(got, handle)
-
-				err = validateSchedulingProfiles(got)
+			if tc.wantErr {
+				require.Error(t, err, "Expected LoadRawConfig to fail")
+				return
 			}
-		}
-		checker(t, "tested function", test, got, err)
-	}
-}
-
-func checker(t *testing.T, function string, test testStruct, got *configapi.EndpointPickerConfig, err error) {
-	checkError(t, function, test, err)
-	if err == nil && !test.wantErr {
-		if diff := cmp.Diff(test.want, got); diff != "" {
-			t.Errorf("In test %s %s returned unexpected response, diff(-want, +got): %v", test.name, function, diff)
-		}
-	}
-}
-
-func checkError(t *testing.T, function string, test testStruct, err error) {
-	if err != nil {
-		if !test.wantErr {
-			t.Fatalf("In test '%s' %s returned unexpected error: %v, want %v", test.name, function, err, test.wantErr)
-		}
-		t.Logf("error was %s", err)
-	} else if test.wantErr {
-		t.Fatalf("In test %s %s did not return an expected error", test.name, function)
+			require.NoError(t, err, "Expected LoadRawConfig to succeed")
+			diff := cmp.Diff(tc.want, got, cmpopts.IgnoreFields(configapi.EndpointPickerConfig{}, "TypeMeta"))
+			require.Empty(t, diff, "Config mismatch (-want +got):\n%s", diff)
+		})
 	}
 }
 
-func TestInstantiatePlugins(t *testing.T) {
-	registerNeededFeatureGates()
-	logger := logging.NewTestLogger()
-	rawConfig, _, err := LoadConfigPhaseOne([]byte(successConfigText), logger)
-	if err != nil {
-		t.Fatalf("LoadConfigPhaseOne returned unexpected error - %v", err)
-	}
-	handle := utils.NewTestHandle(context.Background())
-	_, err = LoadConfigPhaseTwo(rawConfig, handle, logger)
-	if err != nil {
-		t.Fatalf("LoadConfigPhaseTwo returned unexpected error - %v", err)
-	}
-	if len(handle.GetAllPlugins()) == 0 {
-		t.Fatalf("unexpected empty set of loaded plugins")
-	}
-	if t1 := handle.Plugin("test1"); t1 == nil {
-		t.Fatalf("loaded plugins did not contain test1")
-	} else if _, ok := t1.(*test1); !ok {
-		t.Fatalf("loaded plugins returned test1 has the wrong type %#v", t1)
-	}
+// --- Test: Phase 2 (Instantiation, System Defaulting, Deep Validation) ---
 
-	rawConfig, _, err = LoadConfigPhaseOne([]byte(errorBadPluginReferenceParametersText), logger)
-	if err != nil {
-		t.Fatalf("LoadConfigPhaseTwo returned unexpected error - %v", err)
-	}
-	handle = utils.NewTestHandle(context.Background())
-	_, err = LoadConfigPhaseTwo(rawConfig, handle, logger)
-	if err == nil {
-		t.Fatalf("LoadConfigPhaseTwo did not return error as expected ")
-	}
-}
+func TestInstantiateAndConfigure(t *testing.T) {
+	// Not parallel because it modifies global plugin registry.
+	registerTestPlugins(t)
 
-func TestLoadConfig(t *testing.T) {
+	RegisterFeatureGate(datalayer.FeatureGate)
 
 	tests := []struct {
 		name       string
 		configText string
-		want       *config.Config
 		wantErr    bool
+		validate   func(t *testing.T, handle plugins.Handle, cfg *configapi.EndpointPickerConfig)
 	}{
+		// --- Success Scenarios ---
 		{
-			name:       "schedulerSuccess",
+			name:       "Success - Complex Scheduler",
 			configText: successSchedulerConfigText,
 			wantErr:    false,
+			validate: func(t *testing.T, handle plugins.Handle, cfg *configapi.EndpointPickerConfig) {
+				// 1. Verify all explicit plugins exist in the registry
+				require.NotNil(t, handle.Plugin("testScorer"), "Explicit scorer should be instantiated")
+				require.NotNil(t, handle.Plugin("maxScorePicker"), "Explicit picker should be instantiated")
+				require.NotNil(t, handle.Plugin("profileHandler"), "Explicit profile handler should be instantiated")
+
+				// 2. Verify Profile Integrity
+				// We explicitly defined a picker, so the defaulter should NOT have added a second one.
+				require.Len(t, cfg.SchedulingProfiles, 1)
+				require.Len(t, cfg.SchedulingProfiles[0].Plugins, 2,
+					"Profile should have exactly 2 plugins (Scorer + Explicit Picker)")
+
+				// 3. Verify Weight Propagation
+				// The YAML specified weight: 50. Ensure it wasn't overwritten by defaults.
+				scorerRef := cfg.SchedulingProfiles[0].Plugins[0]
+				require.Equal(t, "testScorer", scorerRef.PluginRef)
+				require.NotNil(t, scorerRef.Weight)
+				require.Equal(t, 50, *scorerRef.Weight, "Explicit weight of 50 should be preserved")
+			},
 		},
 		{
-			name:       "successWithNoWeight",
+			name:       "Success - Default Scorer Weight",
 			configText: successWithNoWeightText,
 			wantErr:    false,
+			validate: func(t *testing.T, _ plugins.Handle, cfg *configapi.EndpointPickerConfig) {
+				require.Len(t, cfg.SchedulingProfiles, 1, "Unexpected profile structure")
+				require.Len(t, cfg.SchedulingProfiles[0].Plugins, 2, "Expected Scorer + Default Picker")
+				w := cfg.SchedulingProfiles[0].Plugins[0].Weight
+				require.NotNil(t, w, "Weight should not be nil")
+				require.Equal(t, 1, *w, "Expected default scorer weight of 1")
+			},
 		},
 		{
-			name:       "successWithNoProfileHandlers",
+			name:       "Success - Default Profile Handler Injection",
 			configText: successWithNoProfileHandlersText,
 			wantErr:    false,
+			validate: func(t *testing.T, handle plugins.Handle, cfg *configapi.EndpointPickerConfig) {
+				require.True(t, hasPluginType(handle, profile.SingleProfileHandlerType),
+					"Defaults: SingleProfileHandler was not injected")
+			},
 		},
 		{
-			name:       "successPickerWeightUnsetScorerText",
-			configText: successPickerWeightUnsetScorerText,
+			name:       "Success - Picker Before Scorer",
+			configText: successPickerBeforeScorerText,
 			wantErr:    false,
+			validate: func(t *testing.T, _ plugins.Handle, cfg *configapi.EndpointPickerConfig) {
+				require.Len(t, cfg.SchedulingProfiles, 1)
+				prof := cfg.SchedulingProfiles[0]
+				require.Equal(t, "test-picker", prof.Plugins[0].PluginRef, "Picker should be the first plugin")
+				require.Equal(t, "test-scorer", prof.Plugins[1].PluginRef, "Scorer should be the second plugin")
+				scorerWeight := prof.Plugins[1].Weight
+				require.NotNil(t, scorerWeight, "Scorer weight should be set (defaulted)")
+				require.Equal(t, 1, *scorerWeight, "Scorer weight should default to 1")
+			},
 		},
+
+		// --- Instantiation Errors ---
 		{
-			name:       "errorBadYaml",
-			configText: errorBadYamlText,
+			name:       "Error (Instantiation) - Missing Type Field",
+			configText: errorBadPluginReferenceText,
+			wantErr:    true,
+		},
+		{
+			name:       "Error (Instantiation) - Unknown Plugin Type",
+			configText: errorBadPluginReferencePluginText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorBadPluginJson",
+			name:       "Error (Instantiation) - Invalid JSON Parameters",
 			configText: errorBadPluginJsonText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorNoProfileName",
+			name:       "Error (Instantiation) - Duplicate Plugin Name",
+			configText: errorDuplicatePluginText,
+			wantErr:    true,
+		},
+
+		// --- Deep Validation Errors ---
+		{
+			name:       "Error (Deep Validation) - Missing Profile Name",
 			configText: errorNoProfileNameText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorTwoPickers",
+			name:       "Error (Deep Validation) - Missing PluginRef in Profile",
+			configText: errorBadProfilePluginText,
+			wantErr:    true,
+		},
+		{
+			name:       "Error (Deep Validation) - Profile References Undefined Plugin",
+			configText: errorBadProfilePluginRefText,
+			wantErr:    true,
+		},
+		{
+			name:       "Error (Deep Validation) - Duplicate Profile Name",
+			configText: errorDuplicateProfileText,
+			wantErr:    true,
+		},
+
+		// --- Architectural Errors ---
+		{
+			name:       "Error (Architectural) - Two Pickers in One Profile",
 			configText: errorTwoPickersText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorTwoProfileHandlers",
+			name:       "Error (Deep Architectural) - Multiple Profile Handlers",
 			configText: errorTwoProfileHandlersText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorNoProfileHandlers",
+			name:       "Error (Deep Architectural) - Missing Profile Handler",
 			configText: errorNoProfileHandlersText,
 			wantErr:    true,
 		},
 		{
-			name:       "errorMultiProfilesUseSingleProfileHandler",
+			name:       "Error (Deep Architectural) - Multi-Profile with Single Handler",
 			configText: errorMultiProfilesUseSingleProfileHandlerText,
 			wantErr:    true,
 		},
-		{
-			name:       "errorUnknownFeatureGate",
-			configText: errorUnknownFeatureGateText,
-			wantErr:    true,
-		},
 	}
 
-	registerNeededFeatureGates()
-	registerNeededPlgugins()
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			logger := logging.NewTestLogger()
 
-	logger := logging.NewTestLogger()
-	for _, test := range tests {
-		rawConfig, _, err := LoadConfigPhaseOne([]byte(test.configText), logger)
-		if err != nil {
-			if !test.wantErr {
-				t.Errorf("LoadConfigPhaseOne returned an unexpected error. error %v", err)
-			}
-			t.Logf("error was %s", err)
-		} else {
-			handle := utils.NewTestHandle(context.Background())
-			_, err = LoadConfigPhaseTwo(rawConfig, handle, logger)
+			// 1. Load Raw (Assuming valid yaml/structure for Phase 2 tests)
+			rawConfig, _, err := LoadRawConfig([]byte(tc.configText), logger)
 			if err != nil {
-				if !test.wantErr {
-					t.Errorf("LoadConfigPhaseOne returned an unexpected error. error %v", err)
+				// If we expected failure (and it failed early in Phase 1), success.
+				if tc.wantErr {
+					return
 				}
-				t.Logf("error was %s", err)
-			} else if test.wantErr {
-				t.Errorf("LoadConfig did not return an expected error (%s)", test.name)
+				require.NoError(t, err, "Setup: LoadRawConfig failed")
 			}
-		}
-	}
-}
 
-func registerNeededFeatureGates() {
-	RegisterFeatureGate(datalayer.FeatureGate)
-}
+			// 2. Instantiate & Configure
+			handle := utils.NewTestHandle(context.Background())
+			_, err = InstantiateAndConfigure(rawConfig, handle, logger)
 
-func registerNeededPlgugins() {
-	plugins.Register(prefix.PrefixCachePluginType, prefix.PrefixCachePluginFactory)
-	plugins.Register(picker.MaxScorePickerType, picker.MaxScorePickerFactory)
-	plugins.Register(picker.RandomPickerType, picker.RandomPickerFactory)
-	plugins.Register(picker.WeightedRandomPickerType, picker.WeightedRandomPickerFactory)
-	plugins.Register(profile.SingleProfileHandlerType, profile.SingleProfileHandlerFactory)
+			if tc.wantErr {
+				require.Error(t, err, "Expected InstantiateAndConfigure to fail")
+				return
+			}
+			require.NoError(t, err, "Expected InstantiateAndConfigure to succeed")
+
+			if tc.validate != nil {
+				tc.validate(t, handle, rawConfig)
+			}
+		})
+	}
 }
 
-func TestNewDetector(t *testing.T) {
+// Verify the SaturationConfig builder specifically.
+func TestBuildSaturationConfig(t *testing.T) {
+	t.Parallel()
+
 	tests := []struct {
-		name           string
-		config         *configapi.SaturationDetector
-		expectedConfig saturationdetector.Config
+		name     string
+		input    *configapi.SaturationDetector
+		expected *saturationdetector.Config
 	}{
 		{
-			name: "Valid config",
-			config: &configapi.SaturationDetector{
-				QueueDepthThreshold:       10,
-				KVCacheUtilThreshold:      0.8,
-				MetricsStalenessThreshold: metav1.Duration{Duration: 100 * time.Millisecond},
+			name: "Valid Configuration",
+			input: &configapi.SaturationDetector{
+				QueueDepthThreshold:       20,
+				KVCacheUtilThreshold:      0.9,
+				MetricsStalenessThreshold: metav1.Duration{Duration: 500 * time.Millisecond},
 			},
-			expectedConfig: saturationdetector.Config{
-				QueueDepthThreshold:       10,
-				KVCacheUtilThreshold:      0.8,
-				MetricsStalenessThreshold: 100 * time.Millisecond,
+			expected: &saturationdetector.Config{
+				QueueDepthThreshold:       20,
+				KVCacheUtilThreshold:      0.9,
+				MetricsStalenessThreshold: 500 * time.Millisecond,
 			},
 		},
 		{
-			name: "invalid thresholds, fallback to default",
-			config: &configapi.SaturationDetector{
-				QueueDepthThreshold:       -1,
-				KVCacheUtilThreshold:      -5.0,
-				MetricsStalenessThreshold: metav1.Duration{Duration: 0 * time.Second},
-			},
-			expectedConfig: saturationdetector.Config{
+			name:  "Nil Input (Defaults)",
+			input: nil,
+			expected: &saturationdetector.Config{
 				QueueDepthThreshold:       saturationdetector.DefaultQueueDepthThreshold,
 				KVCacheUtilThreshold:      saturationdetector.DefaultKVCacheUtilThreshold,
 				MetricsStalenessThreshold: saturationdetector.DefaultMetricsStalenessThreshold,
 			},
 		},
 		{
-			name: "kv cache threshold above range, fallback to default",
-			config: &configapi.SaturationDetector{
-				QueueDepthThreshold:       10,
+			name: "Invalid Values (Fallback to Defaults)",
+			input: &configapi.SaturationDetector{
+				QueueDepthThreshold:       -5,
 				KVCacheUtilThreshold:      1.5,
-				MetricsStalenessThreshold: metav1.Duration{Duration: 100 * time.Millisecond},
+				MetricsStalenessThreshold: metav1.Duration{Duration: -10 * time.Second},
 			},
-			expectedConfig: saturationdetector.Config{
-				QueueDepthThreshold:       10,
+			expected: &saturationdetector.Config{
+				QueueDepthThreshold:       saturationdetector.DefaultQueueDepthThreshold,
 				KVCacheUtilThreshold:      saturationdetector.DefaultKVCacheUtilThreshold,
-				MetricsStalenessThreshold: 100 * time.Millisecond,
+				MetricsStalenessThreshold: saturationdetector.DefaultMetricsStalenessThreshold,
 			},
 		},
 	}
 
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			// validate configuration values are loaded from configuration struct properly, including the use of default values when provided value is invalid.
-			sdConfig := loadSaturationDetectorConfig(test.config)
-			if diff := cmp.Diff(test.expectedConfig, *sdConfig); diff != "" {
-				t.Errorf("Unexpected output (-want +got): %v", diff)
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			got := buildSaturationConfig(tc.input)
+			if diff := cmp.Diff(tc.expected, got); diff != "" {
+				t.Errorf("buildSaturationConfig mismatch (-want +got):\n%s", diff)
 			}
 		})
 	}
 }
 
-// The following multi-line string constants, cause false positive lint errors (dupword)
-
-// valid configuration
-//
-//nolint:dupword
-const successConfigText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-- name: profileHandler
-  type: test-profile-handler
-- type: test-two
-  parameters:
-    blockSize: 32
-- name: testPicker
-  type: test-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: test1
-  - pluginRef: test-two
-    weight: 50
-  - pluginRef: testPicker
-featureGates:
-- dataLayer
-saturationDetector:
-  metricsStalenessThreshold: 150ms
-`
-
-// success with missing scheduling profiles
-//
-//nolint:dupword
-const successNoProfilesText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-`
-
-// YAML does not follow expected structure of config
-//
-//nolint:dupword
-const errorBadYamlText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- testing 1 2 3
-`
-
-// missing required Plugin type
-//
-//nolint:dupword
-const errorBadPluginReferenceText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- parameters:
-    a: 1234
-`
-
-// plugin type does not exist
-//
-//nolint:dupword
-const errorBadPluginReferencePluginText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: testx
-  type: test-x
-- name: profileHandler
-  type: test-profile-handler
-`
-
-// missing required scheduling profile name
-//
-//nolint:dupword
-const errorNoProfileNameText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- plugins:
-  - pluginRef: test1
-`
+// --- Helpers & Mocks ---
 
-// missing required plugin reference name, only weight is provided
-//
-//nolint:dupword
-const errorBadProfilePluginText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - weight: 10
-`
-
-// reference a non-existent plugin
-//
-//nolint:dupword
-const errorBadProfilePluginRefText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: plover
-`
-
-// invalid parameters (string provided where int is expected)
-//
-//nolint:dupword
-const errorBadPluginReferenceParametersText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: asdf
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: test1
-`
-
-// duplicate names in plugin list
-//
-//nolint:dupword
-const errorDuplicatePluginText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 20
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: test1
-`
-
-// duplicate scheduling profile name
-//
-//nolint:dupword
-const errorDuplicateProfileText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-- name: test2
-  type: test-one
-  parameters:
-    threshold: 20
-- name: profileHandler
-  type: test-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: test1
-- name: default
-  plugins:
-  - pluginRef: test2
-`
-
-// error with an unknown feature gate
-//
-//nolint:dupword
-const errorUnknownFeatureGateText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: test1
-  type: test-one
-  parameters:
-    threshold: 10
-featureGates:
-- qwerty
-`
-
-// compile-time type validation
-var _ framework.Filter = &test1{}
-
-type test1 struct {
-	typedName plugins.TypedName
-	Threshold int `json:"threshold"`
-}
-
-func newTest1() *test1 {
-	return &test1{
-		typedName: plugins.TypedName{Type: test1Type, Name: "test-1"},
-	}
-}
-
-func (f *test1) TypedName() plugins.TypedName {
-	return f.typedName
-}
-
-// Filter filters out pods that doesn't meet the filter criteria.
-func (f *test1) Filter(_ context.Context, _ *types.CycleState, _ *types.LLMRequest, pods []types.Pod) []types.Pod {
-	return pods
-}
-
-// compile-time type validation
-var _ framework.Scorer = &test2{}
-
-type test2 struct {
-	typedName plugins.TypedName
-}
-
-func newTest2() *test2 {
-	return &test2{
-		typedName: plugins.TypedName{Type: test2Type, Name: "test-2"},
+func hasPluginType(handle plugins.Handle, typeName string) bool {
+	for _, p := range handle.GetAllPlugins() {
+		if p.TypedName().Type == typeName {
+			return true
+		}
 	}
+	return false
 }
 
-func (m *test2) TypedName() plugins.TypedName {
-	return m.typedName
+type mockPlugin struct {
+	t plugins.TypedName
 }
 
-func (m *test2) Score(_ context.Context, _ *types.CycleState, _ *types.LLMRequest, _ []types.Pod) map[types.Pod]float64 {
-	return map[types.Pod]float64{}
-}
-
-// compile-time type validation
-var _ framework.Picker = &testPicker{}
+func (m *mockPlugin) TypedName() plugins.TypedName { return m.t }
 
-type testPicker struct {
-	typedName plugins.TypedName
-}
+// Mock Scorer
+type mockScorer struct{ mockPlugin }
 
-func newTestPicker() *testPicker {
-	return &testPicker{
-		typedName: plugins.TypedName{Type: testPickerType, Name: "test-picker"},
-	}
-}
-
-func (p *testPicker) TypedName() plugins.TypedName {
-	return p.typedName
-}
-
-func (p *testPicker) Pick(_ context.Context, _ *types.CycleState, _ []*types.ScoredPod) *types.ProfileRunResult {
+func (m *mockScorer) Score(context.Context, *types.CycleState, *types.LLMRequest, []types.Pod) map[types.Pod]float64 {
 	return nil
 }
 
-// compile-time type validation
-var _ framework.ProfileHandler = &testProfileHandler{}
-
-type testProfileHandler struct {
-	typedName plugins.TypedName
-}
+// Mock Picker
+type mockPicker struct{ mockPlugin }
 
-func newTestProfileHandler() *testProfileHandler {
-	return &testProfileHandler{
-		typedName: plugins.TypedName{Type: testProfileHandlerType, Name: "test-profile-handler"},
-	}
+func (m *mockPicker) Pick(context.Context, *types.CycleState, []*types.ScoredPod) *types.ProfileRunResult {
+	return nil
 }
 
-func (p *testProfileHandler) TypedName() plugins.TypedName {
-	return p.typedName
-}
+// Mock Handler
+type mockHandler struct{ mockPlugin }
 
-func (p *testProfileHandler) Pick(_ context.Context, _ *types.CycleState, _ *types.LLMRequest, _ map[string]*framework.SchedulerProfile, _ map[string]*types.ProfileRunResult) map[string]*framework.SchedulerProfile {
+func (m *mockHandler) Pick(
+	context.Context,
+	*types.CycleState,
+	*types.LLMRequest,
+	map[string]*framework.SchedulerProfile,
+	map[string]*types.ProfileRunResult,
+) map[string]*framework.SchedulerProfile {
 	return nil
 }
-
-func (p *testProfileHandler) ProcessResults(_ context.Context, _ *types.CycleState, _ *types.LLMRequest, _ map[string]*types.ProfileRunResult) (*types.SchedulingResult, error) {
+func (m *mockHandler) ProcessResults(
+	context.Context,
+	*types.CycleState,
+	*types.LLMRequest,
+	map[string]*types.ProfileRunResult,
+) (*types.SchedulingResult, error) {
 	return nil, nil
 }
 
-func registerTestPlugins() {
-	plugins.Register(test1Type,
-		func(_ string, parameters json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
-			result := newTest1()
-			err := json.Unmarshal(parameters, result)
-			return result, err
-		},
-	)
-
-	plugins.Register(test2Type,
-		func(_ string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
-			return newTest2(), nil
-		},
-	)
-
-	plugins.Register(testPickerType,
-		func(_ string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
-			return newTestPicker(), nil
-		},
-	)
-
-	plugins.Register(testProfileHandlerType,
-		func(_ string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
-			return newTestProfileHandler(), nil
-		},
-	)
-}
-
-// valid configuration
-//
-//nolint:dupword
-const successSchedulerConfigText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: prefixCacheScorer
-  type: prefix-cache-scorer
-  parameters:
-    blockSize: 32
-- name: maxScorePicker
-  type: max-score-picker
-- name: profileHandler
-  type: single-profile-handler
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: prefixCacheScorer
-    weight: 50
-  - pluginRef: maxScorePicker
-featureGates:
-- dataLayer
-`
-
-// valid configuration, with default weight for scorer
-//
-//nolint:dupword
-const successWithNoWeightText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: single-profile-handler
-- name: prefixCacheScorer
-  type: prefix-cache-scorer
-  parameters:
-    blockSize: 32
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: prefixCacheScorer
-`
+func registerTestPlugins(t *testing.T) {
+	t.Helper()
 
-// valid configuration using default profile handler
-//
-//nolint:dupword
-const successWithNoProfileHandlersText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: maxScore
-  type: max-score-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: maxScore
-`
+	// Helper to generate simple factories.
+	register := func(name string, factory plugins.FactoryFunc) {
+		plugins.Register(name, factory)
+	}
 
-// success with picker and unset weight scorer
-//
-//nolint:dupword
-const successPickerWeightUnsetScorerText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- type: random-picker
-- type: prefix-cache-scorer
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: random-picker
-  - pluginRef: prefix-cache-scorer
-`
+	mockFactory := func(tType string) plugins.FactoryFunc {
+		return func(name string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
+			return &mockPlugin{t: plugins.TypedName{Name: name, Type: tType}}, nil
+		}
+	}
 
-// invalid parameter configuration for plugin (string passed, in expected)
-//
-//nolint:dupword
-const errorBadPluginJsonText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: single-profile-handler
-- name: prefixCacheScorer
-  type: prefix-cache-scorer
-  parameters:
-    blockSize: asdf
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: prefixCacheScorer
-    weight: 50
-`
+	// Register standard test mocks.
+	register(testPluginType, mockFactory(testPluginType))
 
-// multiple pickers in scheduling profile
-//
-//nolint:dupword
-const errorTwoPickersText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: single-profile-handler
-- name: maxScore
-  type: max-score-picker
-- name: random
-  type: random-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: maxScore
-  - pluginRef: random
-`
+	plugins.Register(testScorerType, func(name string, params json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
+		// Attempt to unmarshal to trigger errors for invalid JSON in tests.
+		if len(params) > 0 {
+			var p struct {
+				BlockSize int `json:"blockSize"`
+			}
+			if err := json.Unmarshal(params, &p); err != nil {
+				return nil, err
+			}
+		}
+		return &mockScorer{mockPlugin{t: plugins.TypedName{Name: name, Type: testScorerType}}}, nil
+	})
 
-// multiple profile handlers when only one is allowed
-//
-//nolint:dupword
-const errorTwoProfileHandlersText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: single-profile-handler
-- name: secondProfileHandler
-  type: single-profile-handler
-- name: maxScore
-  type: max-score-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: maxScore
-`
+	plugins.Register(testPickerType, func(name string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
+		return &mockPicker{mockPlugin{t: plugins.TypedName{Name: name, Type: testPickerType}}}, nil
+	})
 
-// missing required profile handler
-//
-//nolint:dupword
-const errorNoProfileHandlersText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: maxScore
-  type: max-score-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: maxScore
-- name: prof2
-  plugins:
-  - pluginRef: maxScore
-`
+	plugins.Register(testProfileHandler, func(name string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
+		return &mockHandler{mockPlugin{t: plugins.TypedName{Name: name, Type: testProfileHandler}}}, nil
+	})
 
-// multiple profiles using SingleProfileHandler
-//
-//nolint:dupword
-const errorMultiProfilesUseSingleProfileHandlerText = `
-apiVersion: inference.networking.x-k8s.io/v1alpha1
-kind: EndpointPickerConfig
-plugins:
-- name: profileHandler
-  type: single-profile-handler
-- name: maxScore
-  type: max-score-picker
-schedulingProfiles:
-- name: default
-  plugins:
-  - pluginRef: maxScore
-- name: prof2
-  plugins:
-  - pluginRef: maxScore
-`
+	// Ensure system defaults are registered too.
+	plugins.Register(picker.MaxScorePickerType, picker.MaxScorePickerFactory)
+	plugins.Register(profile.SingleProfileHandlerType, profile.SingleProfileHandlerFactory)
+}
diff --git a/pkg/epp/config/loader/testdata_test.go b/pkg/epp/config/loader/testdata_test.go
new file mode 100644
index 0000000..a2e8adf
--- /dev/null
+++ b/pkg/epp/config/loader/testdata_test.go
@@ -0,0 +1,351 @@
+/*
+Copyright 2025 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 loader
+
+// --- Valid Configurations ---
+
+// successConfigText represents a fully populated, valid configuration.
+// It uses a mix of explicit names and type-derived names.
+const successConfigText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+- name: profileHandler
+  type: test-profile-handler
+- type: test-scorer
+  parameters:
+    blockSize: 32
+- name: testPicker
+  type: test-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: test1
+  - pluginRef: test-scorer
+    weight: 50
+  - pluginRef: testPicker
+featureGates:
+- dataLayer
+saturationDetector:
+  queueDepthThreshold: 10
+  kvCacheUtilThreshold: 0.8
+  metricsStalenessThreshold: 100ms
+`
+
+// successNoProfilesText represents a valid config with plugins but no profiles.
+// The loader should apply the system default profile automatically.
+const successNoProfilesText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+`
+
+// successSchedulerConfigText represents a complex scheduler setup.
+const successSchedulerConfigText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: testScorer
+  type: test-scorer
+  parameters:
+    blockSize: 32
+- name: maxScorePicker
+  type: max-score-picker
+- name: profileHandler
+  type: single-profile-handler
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: testScorer
+    weight: 50
+  - pluginRef: maxScorePicker
+featureGates:
+- dataLayer
+`
+
+// successWithNoWeightText tests that scorers receive the default weight if unspecified.
+const successWithNoWeightText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: single-profile-handler
+- name: testScorer
+  type: test-scorer
+  parameters:
+    blockSize: 32
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: testScorer
+`
+
+// successWithNoProfileHandlersText tests that a default profile handler is injected.
+const successWithNoProfileHandlersText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: maxScore
+  type: max-score-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: maxScore
+`
+
+// successPickerBeforeScorerText tests the regression case where a Picker appears before a Scorer (without weight) in
+// the plugin list.
+const successPickerBeforeScorerText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- type: single-profile-handler
+- type: test-picker
+- type: test-scorer
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: test-picker
+  - pluginRef: test-scorer
+`
+
+// --- Invalid Configurations (Syntax/Structure) ---
+
+// errorBadYamlText contains invalid YAML syntax.
+const errorBadYamlText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- testing 1 2 3
+`
+
+// errorBadPluginReferenceText is missing the required 'type' field.
+const errorBadPluginReferenceText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- parameters:
+    a: 1234
+`
+
+// errorBadPluginReferencePluginText references a plugin type that does not exist in the registry.
+const errorBadPluginReferencePluginText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: testx
+  type: unknown-plugin-type
+- name: profileHandler
+  type: test-profile-handler
+`
+
+// errorBadPluginJsonText has invalid JSON in parameters (string where int expected).
+const errorBadPluginJsonText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: single-profile-handler
+- name: testScorer
+  type: test-scorer
+  parameters:
+    blockSize: asdf
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: testScorer
+    weight: 50
+`
+
+// errorUnknownFeatureGateText includes a feature gate not defined in the code.
+const errorUnknownFeatureGateText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+featureGates:
+- unknown-gate
+`
+
+// --- Invalid Configurations (Logical/Architectural) ---
+
+// errorNoProfileNameText is missing the required profile name.
+const errorNoProfileNameText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+- name: profileHandler
+  type: test-profile-handler
+schedulingProfiles:
+- plugins:
+  - pluginRef: test1
+`
+
+// errorBadProfilePluginText is missing the required pluginRef.
+const errorBadProfilePluginText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: test-profile-handler
+schedulingProfiles:
+- name: default
+  plugins:
+  - weight: 10
+`
+
+// errorBadProfilePluginRefText references a plugin name that wasn't defined.
+const errorBadProfilePluginRefText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: test-profile-handler
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: non-existent-plugin
+`
+
+// errorDuplicatePluginText defines the same plugin name twice.
+const errorDuplicatePluginText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 20
+- name: profileHandler
+  type: test-profile-handler
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: test1
+`
+
+// errorDuplicateProfileText defines the same profile name twice.
+const errorDuplicateProfileText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: test1
+  type: test-plugin
+  parameters:
+    threshold: 10
+- name: test2
+  type: test-plugin
+  parameters:
+    threshold: 20
+- name: profileHandler
+  type: test-profile-handler
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: test1
+- name: default
+  plugins:
+  - pluginRef: test2
+`
+
+// errorTwoPickersText defines multiple pickers in a single profile (invalid).
+const errorTwoPickersText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: single-profile-handler
+- name: maxScore
+  type: max-score-picker
+- name: random
+  type: test-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: maxScore
+  - pluginRef: random
+`
+
+// errorTwoProfileHandlersText defines multiple profile handlers (global singleton).
+const errorTwoProfileHandlersText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: single-profile-handler
+- name: secondProfileHandler
+  type: single-profile-handler
+- name: maxScore
+  type: max-score-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: maxScore
+`
+
+// errorNoProfileHandlersText fails to define any profile handler.
+const errorNoProfileHandlersText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: maxScore
+  type: max-score-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: maxScore
+- name: prof2
+  plugins:
+  - pluginRef: maxScore
+`
+
+// errorMultiProfilesUseSingleProfileHandlerText uses SingleProfileHandler with multiple profiles.
+const errorMultiProfilesUseSingleProfileHandlerText = `
+apiVersion: inference.networking.x-k8s.io/v1alpha1
+kind: EndpointPickerConfig
+plugins:
+- name: profileHandler
+  type: single-profile-handler
+- name: maxScore
+  type: max-score-picker
+schedulingProfiles:
+- name: default
+  plugins:
+  - pluginRef: maxScore
+- name: prof2
+  plugins:
+  - pluginRef: maxScore
+`
diff --git a/pkg/epp/flowcontrol/controller/controller_test.go b/pkg/epp/flowcontrol/controller/controller_test.go
index 9f46374..7fcaa50 100644
--- a/pkg/epp/flowcontrol/controller/controller_test.go
+++ b/pkg/epp/flowcontrol/controller/controller_test.go
@@ -79,7 +79,6 @@ type testHarness struct {
 	// clock is the clock interface used by the controller.
 	clock        clock.WithTicker
 	mockRegistry *mockRegistryClient
-	mockDetector *mocks.MockSaturationDetector
 	// mockClock provides access to FakeClock methods (Step, HasWaiters) if and only if the underlying clock is a
 	// FakeClock.
 	mockClock            *testclock.FakeClock
@@ -91,6 +90,7 @@ type testHarness struct {
 func newUnitHarness(t *testing.T, ctx context.Context, cfg Config, registry *mockRegistryClient) *testHarness {
 	t.Helper()
 	mockDetector := &mocks.MockSaturationDetector{}
+	mockPodLocator := &mocks.MockPodLocator{}
 
 	// Initialize the FakeClock with the current system time.
 	// The controller implementation uses the injected clock to calculate the deadline timestamp,vbut uses the standard
@@ -113,7 +113,7 @@ func newUnitHarness(t *testing.T, ctx context.Context, cfg Config, registry *moc
 		withClock(mockClock),
 		withShardProcessorFactory(mockProcessorFactory.new),
 	}
-	fc, err := NewFlowController(ctx, cfg, registry, mockDetector, logr.Discard(), opts...)
+	fc, err := NewFlowController(ctx, cfg, registry, mockDetector, mockPodLocator, logr.Discard(), opts...)
 	require.NoError(t, err, "failed to create FlowController for unit test harness")
 
 	h := &testHarness{
@@ -121,7 +121,6 @@ func newUnitHarness(t *testing.T, ctx context.Context, cfg Config, registry *moc
 		cfg:                  cfg,
 		clock:                mockClock,
 		mockRegistry:         registry,
-		mockDetector:         mockDetector,
 		mockClock:            mockClock,
 		mockProcessorFactory: mockProcessorFactory,
 	}
@@ -133,8 +132,9 @@ func newUnitHarness(t *testing.T, ctx context.Context, cfg Config, registry *moc
 func newIntegrationHarness(t *testing.T, ctx context.Context, cfg Config, registry *mockRegistryClient) *testHarness {
 	t.Helper()
 	mockDetector := &mocks.MockSaturationDetector{}
-	// Align FakeClock with system time. See explanation in newUnitHarness.
+	mockPodLocator := &mocks.MockPodLocator{}
 
+	// Align FakeClock with system time. See explanation in newUnitHarness.
 	mockClock := testclock.NewFakeClock(time.Now())
 	if registry == nil {
 		registry = &mockRegistryClient{}
@@ -144,7 +144,7 @@ func newIntegrationHarness(t *testing.T, ctx context.Context, cfg Config, regist
 		withRegistryClient(registry),
 		withClock(mockClock),
 	}
-	fc, err := NewFlowController(ctx, cfg, registry, mockDetector, logr.Discard(), opts...)
+	fc, err := NewFlowController(ctx, cfg, registry, mockDetector, mockPodLocator, logr.Discard(), opts...)
 	require.NoError(t, err, "failed to create FlowController for integration test harness")
 
 	h := &testHarness{
@@ -152,7 +152,6 @@ func newIntegrationHarness(t *testing.T, ctx context.Context, cfg Config, regist
 		cfg:          cfg,
 		clock:        mockClock,
 		mockRegistry: registry,
-		mockDetector: mockDetector,
 		mockClock:    mockClock,
 	}
 	return h
@@ -247,6 +246,7 @@ func (f *mockShardProcessorFactory) new(
 	_ context.Context, // The factory does not use the lifecycle context; it's passed to the processor's Run method later.
 	shard contracts.RegistryShard,
 	_ contracts.SaturationDetector,
+	_ contracts.PodLocator,
 	_ clock.WithTicker,
 	_ time.Duration,
 	_ int,
@@ -1001,6 +1001,7 @@ func TestFlowController_WorkerManagement(t *testing.T) {
 			ctx context.Context, // The context created by getOrStartWorker for the potential new processor.
 			shard contracts.RegistryShard,
 			_ contracts.SaturationDetector,
+			_ contracts.PodLocator,
 			_ clock.WithTicker,
 			_ time.Duration,
 			_ int,
diff --git a/pkg/epp/flowcontrol/controller/internal/processor_test.go b/pkg/epp/flowcontrol/controller/internal/processor_test.go
index ff75130..ea7a1f2 100644
--- a/pkg/epp/flowcontrol/controller/internal/processor_test.go
+++ b/pkg/epp/flowcontrol/controller/internal/processor_test.go
@@ -75,6 +75,7 @@ type testHarness struct {
 	clock              *testclock.FakeClock
 	logger             logr.Logger
 	saturationDetector *mocks.MockSaturationDetector
+	podLocator         *mocks.MockPodLocator
 
 	// --- Centralized Mock State ---
 	// The harness's mutex protects the single source of truth for all mock state.
@@ -96,6 +97,7 @@ func newTestHarness(t *testing.T, expiryCleanupInterval time.Duration) *testHarn
 		clock:              testclock.NewFakeClock(time.Now()),
 		logger:             logr.Discard(),
 		saturationDetector: &mocks.MockSaturationDetector{},
+		podLocator:         &mocks.MockPodLocator{Pods: []metrics.PodMetrics{&metrics.FakePodMetrics{}}},
 		startSignal:        make(chan struct{}),
 		queues:             make(map[types.FlowKey]*mocks.MockManagedQueue),
 		priorityFlows:      make(map[int][]types.FlowKey),
@@ -123,6 +125,7 @@ func newTestHarness(t *testing.T, expiryCleanupInterval time.Duration) *testHarn
 		h.ctx,
 		h,
 		h.saturationDetector,
+		h.podLocator,
 		h.clock,
 		expiryCleanupInterval,
 		100,
diff --git a/pkg/epp/requestcontrol/admission_test.go b/pkg/epp/requestcontrol/admission_test.go
index 0857782..388a5c9 100644
--- a/pkg/epp/requestcontrol/admission_test.go
+++ b/pkg/epp/requestcontrol/admission_test.go
@@ -25,6 +25,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/contracts/mocks"
 	fctypes "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/types"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/handlers"
 	schedulingtypes "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/types"
@@ -34,14 +35,6 @@ import (
 
 // --- Mocks ---
 
-type mockSaturationDetector struct {
-	isSaturated bool
-}
-
-func (m *mockSaturationDetector) IsSaturated(_ context.Context, _ []backendmetrics.PodMetrics) bool {
-	return m.isSaturated
-}
-
 type mockFlowController struct {
 	outcome fctypes.QueueOutcome
 	err     error
@@ -56,18 +49,25 @@ func (m *mockFlowController) EnqueueAndWait(
 	return m.outcome, m.err
 }
 
+// --- Legacy Controller Tests ---
+
 func TestLegacyAdmissionController_Admit(t *testing.T) {
 	t.Parallel()
 	ctx := logutil.NewTestLoggerIntoContext(context.Background())
-	candidatePods := []backendmetrics.PodMetrics{}
 	reqCtx := &handlers.RequestContext{
 		SchedulingRequest: &schedulingtypes.LLMRequest{RequestId: "test-req"},
+		Request: &handlers.Request{
+			Metadata: map[string]any{},
+		},
 	}
 
+	mockPods := []backendmetrics.PodMetrics{&backendmetrics.FakePodMetrics{}}
+
 	testCases := []struct {
 		name            string
 		priority        int
 		isSaturated     bool
+		locatorPods     []backendmetrics.PodMetrics
 		expectErr       bool
 		expectErrCode   string
 		expectErrSubstr string
@@ -76,18 +76,30 @@ func TestLegacyAdmissionController_Admit(t *testing.T) {
 			name:        "non_sheddable_saturated_admit",
 			priority:    0,
 			isSaturated: true,
+			locatorPods: mockPods,
 			expectErr:   false,
 		},
 		{
 			name:        "sheddable_not_saturated_admit",
 			priority:    -1,
 			isSaturated: false,
+			locatorPods: mockPods,
 			expectErr:   false,
 		},
 		{
 			name:            "sheddable_saturated_reject",
 			priority:        -1,
 			isSaturated:     true,
+			locatorPods:     mockPods,
+			expectErr:       true,
+			expectErrCode:   errutil.InferencePoolResourceExhausted,
+			expectErrSubstr: "system saturated, sheddable request dropped",
+		},
+		{
+			name:            "sheddable_no_pods_reject",
+			priority:        -1,
+			isSaturated:     true,
+			locatorPods:     []backendmetrics.PodMetrics{},
 			expectErr:       true,
 			expectErrCode:   errutil.InferencePoolResourceExhausted,
 			expectErrSubstr: "system saturated, sheddable request dropped",
@@ -97,10 +109,13 @@ func TestLegacyAdmissionController_Admit(t *testing.T) {
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
-			saturationDetector := &mockSaturationDetector{isSaturated: tc.isSaturated}
-			ac := NewLegacyAdmissionController(saturationDetector)
+			saturationDetector := &mocks.MockSaturationDetector{
+				IsSaturatedFunc: func(context.Context, []backendmetrics.PodMetrics) bool { return tc.isSaturated },
+			}
+			locator := &mocks.MockPodLocator{Pods: tc.locatorPods}
+			ac := NewLegacyAdmissionController(saturationDetector, locator)
 
-			err := ac.Admit(ctx, reqCtx, candidatePods, tc.priority)
+			err := ac.Admit(ctx, reqCtx, tc.priority)
 
 			if !tc.expectErr {
 				assert.NoError(t, err, "Admit() should not have returned an error for scenario: %s", tc.name)
@@ -116,9 +131,10 @@ func TestLegacyAdmissionController_Admit(t *testing.T) {
 	}
 }
 
+// --- Flow Control Controller Tests ---
+
 func TestFlowControlRequestAdapter(t *testing.T) {
 	t.Parallel()
-	candidatePods := []backendmetrics.PodMetrics{&backendmetrics.FakePodMetrics{}}
 
 	testCases := []struct {
 		name            string
@@ -146,59 +162,46 @@ func TestFlowControlRequestAdapter(t *testing.T) {
 				fairnessID:      tc.fairnessID,
 				priority:        tc.priority,
 				requestByteSize: tc.requestByteSize,
-				candidatePods:   candidatePods,
 			}
 
 			assert.Equal(t, tc.requestID, fcReq.ID(), "ID() mismatch")
 			assert.Equal(t, tc.requestByteSize, fcReq.ByteSize(), "ByteSize() mismatch")
-			assert.Equal(t, candidatePods, fcReq.CandidatePodsForScheduling(), "CandidatePodsForScheduling() mismatch")
 			assert.Equal(t, tc.expectFlowKey, fcReq.FlowKey(), "FlowKey() mismatch")
 			assert.Zero(t, fcReq.InitialEffectiveTTL(), "InitialEffectiveTTL() should be zero")
 		})
 	}
 }
+
 func TestFlowControlAdmissionController_Admit(t *testing.T) {
 	t.Parallel()
 	ctx := logutil.NewTestLoggerIntoContext(context.Background())
-	candidatePods := []backendmetrics.PodMetrics{}
-
 	reqCtx := &handlers.RequestContext{
 		SchedulingRequest: &schedulingtypes.LLMRequest{RequestId: "test-req"},
+		Request: &handlers.Request{
+			Metadata: map[string]any{},
+		},
 	}
 
 	testCases := []struct {
 		name            string
 		priority        int
-		isSaturated     bool
 		fcOutcome       fctypes.QueueOutcome
 		fcErr           error
 		expectErr       bool
 		expectErrCode   string
 		expectErrSubstr string
-		expectFCSkipped bool
 	}{
 		{
-			name:            "sheddable_saturated_reject",
-			priority:        -1,
-			isSaturated:     true,
-			expectErr:       true,
-			expectErrCode:   errutil.InferencePoolResourceExhausted,
-			expectErrSubstr: "system saturated, sheddable request dropped",
-			expectFCSkipped: true,
-		},
-		{
-			name:        "sheddable_not_saturated_dispatch",
-			priority:    -1,
-			isSaturated: false,
-			fcOutcome:   fctypes.QueueOutcomeDispatched,
-			expectErr:   false,
+			name:      "sheddable_dispatched",
+			priority:  -1,
+			fcOutcome: fctypes.QueueOutcomeDispatched,
+			expectErr: false,
 		},
 		{
-			name:        "non_sheddable_saturated_dispatch",
-			priority:    0,
-			isSaturated: true,
-			fcOutcome:   fctypes.QueueOutcomeDispatched,
-			expectErr:   false,
+			name:      "non_sheddable_dispatched",
+			priority:  0,
+			fcOutcome: fctypes.QueueOutcomeDispatched,
+			expectErr: false,
 		},
 		{
 			name:            "fc_reject_capacity",
@@ -255,17 +258,12 @@ func TestFlowControlAdmissionController_Admit(t *testing.T) {
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
-			sd := &mockSaturationDetector{isSaturated: tc.isSaturated}
 			fc := &mockFlowController{outcome: tc.fcOutcome, err: tc.fcErr}
-			ac := NewFlowControlAdmissionController(sd, fc)
+			ac := NewFlowControlAdmissionController(fc)
 
-			err := ac.Admit(ctx, reqCtx, candidatePods, tc.priority)
+			err := ac.Admit(ctx, reqCtx, tc.priority)
 
-			if tc.expectFCSkipped {
-				assert.False(t, fc.called, "FlowController should not have been called for scenario: %s", tc.name)
-			} else {
-				assert.True(t, fc.called, "FlowController should have been called for scenario: %s", tc.name)
-			}
+			assert.True(t, fc.called, "FlowController should have been called for scenario: %s", tc.name)
 
 			if !tc.expectErr {
 				assert.NoError(t, err, "Admit() returned an unexpected error for scenario: %s", tc.name)
diff --git a/pkg/epp/requestcontrol/director_test.go b/pkg/epp/requestcontrol/director_test.go
index 58372e7..9b583cf 100644
--- a/pkg/epp/requestcontrol/director_test.go
+++ b/pkg/epp/requestcontrol/director_test.go
@@ -60,12 +60,7 @@ type mockAdmissionController struct {
 	admitErr error
 }
 
-func (m *mockAdmissionController) Admit(
-	_ context.Context,
-	_ *handlers.RequestContext,
-	_ []backendmetrics.PodMetrics,
-	_ int,
-) error {
+func (m *mockAdmissionController) Admit(context.Context, *handlers.RequestContext, int) error {
 	return m.admitErr
 }
 
@@ -673,6 +668,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 					director.datastore = mockDs
 					director.podLocator = NewCachedPodLocator(context.Background(), NewDatastorePodLocator(mockDs), time.Minute)
 				}
+
 				reqCtx := &handlers.RequestContext{
 					Request: &handlers.Request{
 						// Create a copy of the map for each test run to avoid mutation issues.
diff --git a/test/integration/epp/hermetic_test.go b/test/integration/epp/hermetic_test.go
index a1aff76..a3557a6 100644
--- a/test/integration/epp/hermetic_test.go
+++ b/test/integration/epp/hermetic_test.go
@@ -1240,14 +1240,13 @@ func BeforeSuite() func() {
 	}
 	detector := saturationdetector.NewDetector(sdConfig, logger.WithName("saturation-detector"))
 	serverRunner.SaturationDetector = detector
-	admissionController := requestcontrol.NewLegacyAdmissionController(detector)
 	locator := requestcontrol.NewDatastorePodLocator(serverRunner.Datastore)
-	cachedLocator := requestcontrol.NewCachedPodLocator(context.Background(), locator, time.Millisecond*50)
+	admissionController := requestcontrol.NewLegacyAdmissionController(detector, locator)
 	serverRunner.Director = requestcontrol.NewDirectorWithConfig(
 		serverRunner.Datastore,
 		scheduler,
 		admissionController,
-		cachedLocator,
+		locator,
 		requestcontrol.NewConfig(),
 	)
 	serverRunner.SecureServing = false