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

Issue:
Question: MinItems/MaxItems on map (LabelSelector.MatchLabels) causing CRD gen error.
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!

If the matter is security related, please disclose it privately via https://kubernetes.io/security/
-->

**What happened**:

When importing GIE as a Go dependency and running `controller-gen` crd on code that uses GIE types, CRD generation fails:
```
must apply minitems to an array
must apply maxitem to an array
```

**Root cause**

`LabelSelector.MatchLabels `is a map but uses `MinItems/MaxItems` validation markers:
https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/v1.0.0/api/v1/shared_types.go#L134-L143
```go
type LabelSelector struct {
    // +kubebuilder:validation:MinItems=1
    // +kubebuilder:validation:MaxItems=64
    MatchLabels map[string]LabelValue `json:"matchLabels"`
}
```
Per controller-tools, `MinItems/MaxItems` apply to arrays, not maps:
https://github.com/kubernetes-sigs/controller-tools/blob/5c2d62552c97a0139c1dfdc6276c69f2a5f6120d/pkg/crd/markers/validation.go#L478C1-L495C1


**What you expected to happen**:
CRD generation succeeds. If cardinality on a map is intended, I believe MinProperties/MaxProperties would be the correct markers, or the map cardinality check could be omitted.

**How to reproduce it (as minimally and precisely as possible)**:
1. Add sigs.k8s.io/gateway-api-inference-extension v1.0.0 or v1.0.1 to go.mod.
2. Define types that embed or reference GIE types (e.g., LabelSelector).
3. Run: `controller-gen crd paths=./your/package/...`
4. Observe the validation errors above.
Example: OpenDataHub KServe hit this when generating CRDs for LLM inference services that use GIE types.

**Anything else we need to know?**:
**Why GIE CI did not catch this?**
The Makefile only runs the object generator: `controller-gen object:headerFile="..." paths="./..."`
The object generator creates DeepCopy methods and does not validate CRD markers.

hack/verify-manifests.sh in v1.0.1 validates pre-generated YAML but does not re-run controller-gen with crd, so invalid markers are not checked:
https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/v1.0.1/hack/verify-manifests.sh

**Environment**:
- GIE version: v1.0.0, v1.0.1
- controller-gen version: v0.16.1+, v0.17.2+ (consistent behavior)
- Affected: Any downstream project importing GIE and running controller-gen crd


Hints from issue comments:
@ahg-g: I am not sure I am following, if CRD generation is failing, presubmit on open PRs should as well. Where does CRD generation fail?@KillianGolds: > I am not sure I am following, if CRD generation is failing, presubmit on open PRs should as well. Where does CRD generation fail?

Hi @ahg-g, sorry I definitely could of been more clearer in the issue itself. 

The failure occurs in downstream consumers (like OpenDataHub KServe) when they import GIE v1.0.0 as a Go dependency and run their own CRD generation.  

**The Issue:**  
The bug is in GIE's Go source:  
https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/v1.0.0/api/v1/shared_types.go#L134-L143  

```go
type LabelSelector struct {
    // +kubebuilder:validation:MinItems=1
    // +kubebuilder:validation:MaxItems=64
    MatchLabels map[string]LabelValue `json:"matchLabels"`
}
```
`MatchLabels` is a map but has `MinItems` and `MaxItems` markers, which only work on arrays. ---

**When Does It Fail**

KServe imports GIE as a dependency  
(https://github.com/opendatahub-io/kserve/blob/RHOAIENG-34472-dev/go.mod#L52)

When KServe runs make manifests
(https://github.com/opendatahub-io/kserve/blob/RHOAIENG-34472-dev/Makefile#L85

```make
controller-gen paths=./pkg/apis/serving/...
```

`controller-gen` processes KServe’s types (which embed or reference GIE types) and validates all `kubebuilder` markers, including those in imported dependencies.
It correctly errors:
```
Error: must apply minitems to an array
Error: must apply maxitems to an array
```

**Why GIE's Presubmit Doesn't Catch It**

GIE's `verify-crds` target runs  
https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/v1.0.1/hack/verify-manifests.sh, which:

- Copies pre-generated CRD YAML files from `config/crd/bases/`
- Validates them using `kubectl-validate`
- Does **not** run `controller-gen` to regenerate CRDs from the Go source

In doing so the validation step never processes the incorrect kubebuilder markers in the Go code.

@pierDipi: To add more info, GIE uses it's own Go-based generator https://github.com/kubernetes-sigs/gateway-api-inference-extension/tree/main/pkg/generator

if you run the _CRD generator of controller-gen_ in the root directory of GIE, you will get the error

```shell
$ ./bin/controller-gen crd object:headerFile="hack/boilerplate/boilerplate.generatego.txt" paths="./..." 
.../gateway-api-inference-extension/api/v1/shared_types.go:142:2: must apply minitems to an array
.../gateway-api-inference-extension/api/v1/shared_types.go:142:2: must apply maxitem to an array
Error: not all generators ran successfully
run `controller-gen crd object:headerFile=hack/boilerplate/boilerplate.generatego.txt paths=./... -w` to see all available markers, or `controller-gen crd object:headerFile=hack/boilerplate/boilerplate.generatego.txt paths=./... -h` for usage
```@nirrozenbaum: @pierDipi @KillianGolds just to verify, have you tested after your fix that controller gen works with no errors?@KillianGolds: > [@pierDipi](https://github.com/pierDipi) [@KillianGolds](https://github.com/KillianGolds) just to verify, have you tested after your fix that controller gen works with no errors?

Yes, confirmed! I've tested controller-gen after the fix with multiple versions:

**1. Direct Testing:**
- **controller-gen v0.16.1** (Makefile pinned version): Exit code 0 ✓
- **controller-gen v0.19.0** (go.mod version): Exit code 0 ✓
- Both versions correctly generate `minProperties: 1` and `maxProperties: 64` ✓

**2. Downstream Testing in KServe (Go 1.24.1):**
I originally discovered this issue while working on https://github.com/opendatahub-io/kserve/pull/914,
where KServe depends on gateway-api-inference-extension. After applying this fix:
- KServe's controller-gen can now successfully generate CRDs ✓
- The original error is resolved:
  ```
   must apply minitems to an array
   must apply maxitem to an array
  ```

**The fix is version-independent** because MinProperties/MaxProperties is the correct marker syntax
for map fields across all controller-gen versions. This issue would manifest with any version of
controller-tools.


Solution patch:
diff --git a/Makefile b/Makefile
index c483be7..16dad0a 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,8 @@ SHELL = /usr/bin/env bash -o pipefail
 
 GIT_COMMIT_SHA ?= "$(shell git rev-parse HEAD 2>/dev/null)"
 GIT_TAG ?= $(shell git describe --tags --dirty --always)
-PLATFORMS ?= linux/amd64
+TARGETARCH ?= $(shell go env GOARCH)
+PLATFORMS ?= linux/$(TARGETARCH)
 DOCKER_BUILDX_CMD ?= docker buildx
 IMAGE_BUILD_CMD ?= $(DOCKER_BUILDX_CMD) build
 IMAGE_BUILD_EXTRA_OPTS ?=
@@ -99,6 +100,7 @@ help: ## Display this help.
 .PHONY: generate
 generate: controller-gen code-generator ## Generate WebhookConfiguration, ClusterRole, CustomResourceDefinition objects, code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
 	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate/boilerplate.generatego.txt" paths="./..."
+	$(CONTROLLER_GEN) crd output:dir="./config/crd/bases" paths="./..."
 	./hack/update-codegen.sh
 
 # Use same code-generator version as k8s.io/api
@@ -307,13 +309,24 @@ live-docs:
 	docker build -t gaie/mkdocs hack/mkdocs/image
 	docker run --rm -it -p 3000:3000 -v ${PWD}:/docs gaie/mkdocs
 
-.PHONY: apix-ref-docs
-apix-ref-docs: crd-ref-docs
+.PHONY: api-ref-docs-all
+api-ref-docs-all: apix-v1a1-ref-docs apix-v1a2-ref-docs api-ref-docs
+
+.PHONY: apix-v1a1-ref-docs
+apix-v1a1-ref-docs: crd-ref-docs
+	${CRD_REF_DOCS} \
+		--source-path=${PWD}/apix/v1alpha1 \
+		--config=crd-ref-docs.yaml \
+		--renderer=markdown \
+		--output-path=${PWD}/site-src/reference/x-v1a1-spec.md
+
+.PHONY: apix-v1a2-ref-docs
+apix-v1a2-ref-docs: crd-ref-docs
 	${CRD_REF_DOCS} \
 		--source-path=${PWD}/apix/v1alpha2 \
 		--config=crd-ref-docs.yaml \
 		--renderer=markdown \
-		--output-path=${PWD}/site-src/reference/x-spec.md
+		--output-path=${PWD}/site-src/reference/x-v1a2-spec.md
 
 .PHONY: api-ref-docs
 api-ref-docs: crd-ref-docs
@@ -384,7 +397,7 @@ GCI = $(LOCALBIN)/gci
 
 ## Tool Versions
 KUSTOMIZE_VERSION ?= v5.4.3
-CONTROLLER_TOOLS_VERSION ?= v0.16.1
+CONTROLLER_TOOLS_VERSION ?= v0.19.0
 ENVTEST_VERSION ?= release-0.19
 CRD_REF_DOCS_VERSION ?= v0.2.0
 GOLANGCI_LINT_VERSION ?= v2.3.0
diff --git a/api/v1/inferencepool_types.go b/api/v1/inferencepool_types.go
index 76026df..92e7aff 100644
--- a/api/v1/inferencepool_types.go
+++ b/api/v1/inferencepool_types.go
@@ -202,8 +202,42 @@ type ParentStatus struct {
 	//
 	// +required
 	ParentRef ParentReference `json:"parentRef,omitzero"`
+
+	// ControllerName is a domain/path string that indicates the name of the controller that
+	// wrote this status. This corresponds with the GatewayClass controllerName field when the
+	// parentRef references a Gateway kind.
+	//
+	// Example: "example.net/gateway-controller".
+	//
+	// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names:
+	//
+	//  https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+	//
+	// Controllers MAY populate this field when writing status. When populating this field, controllers
+	// should ensure that entries to status populated with their ControllerName are cleaned up when they
+	// are no longer necessary.
+	//
+	// +optional
+	ControllerName ControllerName `json:"controllerName,omitempty"`
 }
 
+// ControllerName is the name of a controller that manages ParentStatus. It must be a domain prefixed
+// path.
+//
+// Valid values include:
+//
+// * "example.com/bar"
+//
+// Invalid values include:
+//
+// * "example.com" - must include path
+// * "foo.example.com" - must include path
+//
+// +kubebuilder:validation:MinLength=1
+// +kubebuilder:validation:MaxLength=253
+// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
+type ControllerName string
+
 // InferencePoolConditionType is a type of status condition for the InferencePool.
 type InferencePoolConditionType string
 
@@ -274,6 +308,38 @@ const (
 	InferencePoolReasonInvalidExtensionRef InferencePoolReason = "InvalidExtensionRef"
 )
 
+const (
+	// InferencePoolConditionExported is a type of condition that indicates whether the
+	// controller was able to export the InferencePool to the specified clusters.
+	//
+	// Possible reasons for this condition to be True are:
+	//
+	// * "Exported"
+	//
+	// Possible reasons for this condition to be False are:
+	//
+	// * "NotRequested"
+	// * "NotSupported"
+	//
+	// Controllers MAY raise this condition with other reasons, but should
+	// prefer to use the reasons listed above to improve interoperability.
+	InferencePoolConditionExported InferencePoolConditionType = "Exported"
+
+	// InferencePoolReasonExported is a reason used with the "Exported" condition when the
+	// condition is true.
+	InferencePoolReasonExported InferencePoolReason = "Exported"
+
+	// InferencePoolReasonNotRequested is a reason used with the "Exported" condition when the
+	// condition is false and no export was requested by the InferencePool. This indicates a
+	// deliberate non-action rather than an error.
+	InferencePoolReasonNotRequested InferencePoolReason = "NotRequested"
+
+	// InferencePoolReasonNotSupported is a reason used with the "Exported" condition when the
+	// condition is false and the export was requested but is not supported by the implementation.
+	// Controllers should include details in the condition message.
+	InferencePoolReasonNotSupported InferencePoolReason = "NotSupported"
+)
+
 // ParentReference identifies an API object. It is used to associate the InferencePool with a
 // parent resource, such as a Gateway.
 type ParentReference struct {
diff --git a/api/v1/shared_types.go b/api/v1/shared_types.go
index bc315fd..5d5d3e7 100644
--- a/api/v1/shared_types.go
+++ b/api/v1/shared_types.go
@@ -137,7 +137,7 @@ type LabelSelector struct {
 	// The matching logic is an AND operation on all entries.
 	//
 	// +required
-	// +kubebuilder:validation:MinItems=1
-	// +kubebuilder:validation:MaxItems=64
+	// +kubebuilder:validation:MinProperties=1
+	// +kubebuilder:validation:MaxProperties=64
 	MatchLabels map[LabelKey]LabelValue `json:"matchLabels,omitempty"`
 }
diff --git a/apix/v1alpha1/doc.go b/apix/v1alpha1/doc.go
new file mode 100644
index 0000000..122c3b9
--- /dev/null
+++ b/apix/v1alpha1/doc.go
@@ -0,0 +1,22 @@
+/*
+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 v1alpha1 contains API Schema definitions for the
+// inference.networking.x-k8s.io API group.
+//
+// +kubebuilder:object:generate=true
+// +groupName=inference.networking.x-k8s.io
+package v1alpha1
diff --git a/apix/v1alpha1/inferencepoolimport_types.go b/apix/v1alpha1/inferencepoolimport_types.go
new file mode 100644
index 0000000..2239aa3
--- /dev/null
+++ b/apix/v1alpha1/inferencepoolimport_types.go
@@ -0,0 +1,143 @@
+/*
+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 v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
+)
+
+// InferencePoolImport is the Schema for the InferencePoolImports API.
+//
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:shortName=infpimp
+// +kubebuilder:subresource:status
+// +kubebuilder:storageversion
+// +genclient
+type InferencePoolImport struct {
+	metav1.TypeMeta `json:",inline"`
+
+	// +optional
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// Status defines the observed state of the InferencePoolImport.
+	//
+	// +optional
+	//nolint:kubeapilinter // status should not be a pointer.
+	Status InferencePoolImportStatus `json:"status,omitempty"`
+}
+
+// InferencePoolImportList contains a list of InferencePoolImports.
+//
+// +kubebuilder:object:root=true
+type InferencePoolImportList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []InferencePoolImport `json:"items"`
+}
+
+// InferencePoolImportStatus defines the observed state of the InferencePoolImport.
+type InferencePoolImportStatus struct {
+	// Controllers is a list of controllers that are responsible for managing the InferencePoolImport.
+	//
+	// +listType=map
+	// +listMapKey=name
+	// +kubebuilder:validation:MaxItems=8
+	// +kubebuilder:validation:Required
+	Controllers []ImportController `json:"controllers"`
+}
+
+// ImportController defines a controller that is responsible for managing the InferencePoolImport.
+type ImportController struct {
+	// Name is a domain/path string that indicates the name of the controller that manages the
+	// InferencePoolImport. Name corresponds to the GatewayClass controllerName field when the
+	// controller will manage parents of type "Gateway". Otherwise, the name is implementation-specific.
+	//
+	// Example: "example.net/import-controller".
+	//
+	// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes
+	// names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).
+	//
+	// A controller MUST populate this field when writing status and ensure that entries to status
+	// populated with their controller name are removed when they are no longer necessary.
+	//
+	// +required
+	Name ControllerName `json:"name"`
+
+	// ExportingClusters is a list of clusters that exported the InferencePool(s) that back the
+	// InferencePoolImport. Required when the controller is responsible for CRUD'ing the InferencePoolImport
+	// from the exported InferencePool(s).
+	//
+	// +optional
+	ExportingClusters []ExportingCluster `json:"exportingClusters,omitempty"`
+
+	// Parents is a list of parent resources, typically Gateways, that are associated with the
+	// InferencePoolImport, and the status of the InferencePoolImport with respect to each parent.
+	//
+	// Ancestor would be a more accurate name, but Parent is consistent with InferencePool terminology.
+	//
+	// Required when the controller manages the InferencePoolImport as an HTTPRoute backendRef. The controller
+	// must add an entry for each parent it manages and remove the parent entry when the controller no longer
+	// considers the InferencePoolImport to be associated with that parent.
+	//
+	// +optional
+	// +listType=atomic
+	Parents []v1.ParentStatus `json:"parents,omitempty"`
+
+	// Conditions track the state of the InferencePoolImport.
+	//
+	// Known condition types are:
+	//
+	//  * "Accepted"
+	//
+	// +optional
+	// +listType=map
+	// +listMapKey=type
+	// +kubebuilder:validation:MaxItems=8
+	Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+
+// ControllerName is the name of a controller that manages a resource. It must be a domain prefixed path.
+//
+// Valid values include:
+//
+//   - "example.com/bar"
+//
+// Invalid values include:
+//
+//   - "example.com" - must include path
+//   - "foo.example.com" - must include path
+//
+// +kubebuilder:validation:MinLength=1
+// +kubebuilder:validation:MaxLength=253
+// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
+type ControllerName string
+
+// ClusterName is the name of a cluster that exported the InferencePool.
+//
+// +kubebuilder:validation:MinLength=1
+// +kubebuilder:validation:MaxLength=253
+type ClusterName string
+
+// ExportingCluster defines a cluster that exported the InferencePool that backs this InferencePoolImport.
+type ExportingCluster struct {
+	// Name of the exporting cluster (must be unique within the list).
+	//
+	// +kubebuilder:validation:Required
+	Name ClusterName `json:"name"`
+}
diff --git a/apix/v1alpha1/shared_types.go b/apix/v1alpha1/shared_types.go
new file mode 100644
index 0000000..56fd71d
--- /dev/null
+++ b/apix/v1alpha1/shared_types.go
@@ -0,0 +1,24 @@
+/*
+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 v1alpha1
+
+// ExportAnnotationKey is the annotation key used to export an InferencePool.
+var ExportAnnotationKey = "inference.networking.x-k8s.io/export"
+
+// ExportAnnotationVal is the annotation value used to export an InferencePool
+// to all clusters.
+var ExportAnnotationVal = "ClusterSet"
diff --git a/apix/v1alpha1/zz_generated.deepcopy.go b/apix/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..f6f1826
--- /dev/null
+++ b/apix/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,156 @@
+//go:build !ignore_autogenerated
+
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/gateway-api-inference-extension/api/v1"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExportingCluster) DeepCopyInto(out *ExportingCluster) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExportingCluster.
+func (in *ExportingCluster) DeepCopy() *ExportingCluster {
+	if in == nil {
+		return nil
+	}
+	out := new(ExportingCluster)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ImportController) DeepCopyInto(out *ImportController) {
+	*out = *in
+	if in.ExportingClusters != nil {
+		in, out := &in.ExportingClusters, &out.ExportingClusters
+		*out = make([]ExportingCluster, len(*in))
+		copy(*out, *in)
+	}
+	if in.Parents != nil {
+		in, out := &in.Parents, &out.Parents
+		*out = make([]v1.ParentStatus, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]metav1.Condition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImportController.
+func (in *ImportController) DeepCopy() *ImportController {
+	if in == nil {
+		return nil
+	}
+	out := new(ImportController)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InferencePoolImport) DeepCopyInto(out *InferencePoolImport) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InferencePoolImport.
+func (in *InferencePoolImport) DeepCopy() *InferencePoolImport {
+	if in == nil {
+		return nil
+	}
+	out := new(InferencePoolImport)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *InferencePoolImport) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InferencePoolImportList) DeepCopyInto(out *InferencePoolImportList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]InferencePoolImport, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InferencePoolImportList.
+func (in *InferencePoolImportList) DeepCopy() *InferencePoolImportList {
+	if in == nil {
+		return nil
+	}
+	out := new(InferencePoolImportList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *InferencePoolImportList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InferencePoolImportStatus) DeepCopyInto(out *InferencePoolImportStatus) {
+	*out = *in
+	if in.Controllers != nil {
+		in, out := &in.Controllers, &out.Controllers
+		*out = make([]ImportController, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InferencePoolImportStatus.
+func (in *InferencePoolImportStatus) DeepCopy() *InferencePoolImportStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(InferencePoolImportStatus)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/apix/v1alpha1/zz_generated.register.go b/apix/v1alpha1/zz_generated.register.go
new file mode 100644
index 0000000..4894b76
--- /dev/null
+++ b/apix/v1alpha1/zz_generated.register.go
@@ -0,0 +1,70 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by register-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// GroupName specifies the group name used to register the objects.
+const GroupName = "inference.networking.x-k8s.io"
+
+// GroupVersion specifies the group and the version used to register the objects.
+var GroupVersion = v1.GroupVersion{Group: GroupName, Version: "v1alpha1"}
+
+// SchemeGroupVersion is group version used to register these objects
+// Deprecated: use GroupVersion instead.
+var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+	return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+var (
+	// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
+	SchemeBuilder      runtime.SchemeBuilder
+	localSchemeBuilder = &SchemeBuilder
+	// Deprecated: use Install instead
+	AddToScheme = localSchemeBuilder.AddToScheme
+	Install     = localSchemeBuilder.AddToScheme
+)
+
+func init() {
+	// We only register manually written functions here. The registration of the
+	// generated functions takes place in the generated files. The separation
+	// makes the code compile even when the generated files are missing.
+	localSchemeBuilder.Register(addKnownTypes)
+}
+
+// Adds the list of known types to Scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&InferencePoolImport{},
+		&InferencePoolImportList{},
+	)
+	// AddToGroupVersion allows the serialization of client types like ListOptions.
+	v1.AddToGroupVersion(scheme, SchemeGroupVersion)
+	return nil
+}
diff --git a/client-go/applyconfiguration/api/v1/parentstatus.go b/client-go/applyconfiguration/api/v1/parentstatus.go
index 5accb0f..929df58 100644
--- a/client-go/applyconfiguration/api/v1/parentstatus.go
+++ b/client-go/applyconfiguration/api/v1/parentstatus.go
@@ -20,13 +20,15 @@ package v1
 
 import (
 	metav1 "k8s.io/client-go/applyconfigurations/meta/v1"
+	apiv1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
 )
 
 // ParentStatusApplyConfiguration represents a declarative configuration of the ParentStatus type for use
 // with apply.
 type ParentStatusApplyConfiguration struct {
-	Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"`
-	ParentRef  *ParentReferenceApplyConfiguration   `json:"parentRef,omitempty"`
+	Conditions     []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"`
+	ParentRef      *ParentReferenceApplyConfiguration   `json:"parentRef,omitempty"`
+	ControllerName *apiv1.ControllerName                `json:"controllerName,omitempty"`
 }
 
 // ParentStatusApplyConfiguration constructs a declarative configuration of the ParentStatus type for use with
@@ -55,3 +57,11 @@ func (b *ParentStatusApplyConfiguration) WithParentRef(value *ParentReferenceApp
 	b.ParentRef = value
 	return b
 }
+
+// WithControllerName sets the ControllerName field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ControllerName field is set to the value of the last call.
+func (b *ParentStatusApplyConfiguration) WithControllerName(value apiv1.ControllerName) *ParentStatusApplyConfiguration {
+	b.ControllerName = &value
+	return b
+}
diff --git a/client-go/applyconfiguration/apix/v1alpha1/exportingcluster.go b/client-go/applyconfiguration/apix/v1alpha1/exportingcluster.go
new file mode 100644
index 0000000..d1b2db0
--- /dev/null
+++ b/client-go/applyconfiguration/apix/v1alpha1/exportingcluster.go
@@ -0,0 +1,43 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+)
+
+// ExportingClusterApplyConfiguration represents a declarative configuration of the ExportingCluster type for use
+// with apply.
+type ExportingClusterApplyConfiguration struct {
+	Name *apixv1alpha1.ClusterName `json:"name,omitempty"`
+}
+
+// ExportingClusterApplyConfiguration constructs a declarative configuration of the ExportingCluster type for use with
+// apply.
+func ExportingCluster() *ExportingClusterApplyConfiguration {
+	return &ExportingClusterApplyConfiguration{}
+}
+
+// WithName sets the Name field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Name field is set to the value of the last call.
+func (b *ExportingClusterApplyConfiguration) WithName(value apixv1alpha1.ClusterName) *ExportingClusterApplyConfiguration {
+	b.Name = &value
+	return b
+}
diff --git a/client-go/applyconfiguration/apix/v1alpha1/importcontroller.go b/client-go/applyconfiguration/apix/v1alpha1/importcontroller.go
new file mode 100644
index 0000000..1c30787
--- /dev/null
+++ b/client-go/applyconfiguration/apix/v1alpha1/importcontroller.go
@@ -0,0 +1,87 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/client-go/applyconfigurations/meta/v1"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+	v1 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/api/v1"
+)
+
+// ImportControllerApplyConfiguration represents a declarative configuration of the ImportController type for use
+// with apply.
+type ImportControllerApplyConfiguration struct {
+	Name              *apixv1alpha1.ControllerName         `json:"name,omitempty"`
+	ExportingClusters []ExportingClusterApplyConfiguration `json:"exportingClusters,omitempty"`
+	Parents           []v1.ParentStatusApplyConfiguration  `json:"parents,omitempty"`
+	Conditions        []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"`
+}
+
+// ImportControllerApplyConfiguration constructs a declarative configuration of the ImportController type for use with
+// apply.
+func ImportController() *ImportControllerApplyConfiguration {
+	return &ImportControllerApplyConfiguration{}
+}
+
+// WithName sets the Name field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Name field is set to the value of the last call.
+func (b *ImportControllerApplyConfiguration) WithName(value apixv1alpha1.ControllerName) *ImportControllerApplyConfiguration {
+	b.Name = &value
+	return b
+}
+
+// WithExportingClusters adds the given value to the ExportingClusters field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the ExportingClusters field.
+func (b *ImportControllerApplyConfiguration) WithExportingClusters(values ...*ExportingClusterApplyConfiguration) *ImportControllerApplyConfiguration {
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithExportingClusters")
+		}
+		b.ExportingClusters = append(b.ExportingClusters, *values[i])
+	}
+	return b
+}
+
+// WithParents adds the given value to the Parents field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the Parents field.
+func (b *ImportControllerApplyConfiguration) WithParents(values ...*v1.ParentStatusApplyConfiguration) *ImportControllerApplyConfiguration {
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithParents")
+		}
+		b.Parents = append(b.Parents, *values[i])
+	}
+	return b
+}
+
+// WithConditions adds the given value to the Conditions field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the Conditions field.
+func (b *ImportControllerApplyConfiguration) WithConditions(values ...*metav1.ConditionApplyConfiguration) *ImportControllerApplyConfiguration {
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithConditions")
+		}
+		b.Conditions = append(b.Conditions, *values[i])
+	}
+	return b
+}
diff --git a/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimport.go b/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimport.go
new file mode 100644
index 0000000..f29c038
--- /dev/null
+++ b/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimport.go
@@ -0,0 +1,233 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	v1 "k8s.io/client-go/applyconfigurations/meta/v1"
+)
+
+// InferencePoolImportApplyConfiguration represents a declarative configuration of the InferencePoolImport type for use
+// with apply.
+type InferencePoolImportApplyConfiguration struct {
+	v1.TypeMetaApplyConfiguration    `json:",inline"`
+	*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
+	Status                           *InferencePoolImportStatusApplyConfiguration `json:"status,omitempty"`
+}
+
+// InferencePoolImport constructs a declarative configuration of the InferencePoolImport type for use with
+// apply.
+func InferencePoolImport(name, namespace string) *InferencePoolImportApplyConfiguration {
+	b := &InferencePoolImportApplyConfiguration{}
+	b.WithName(name)
+	b.WithNamespace(namespace)
+	b.WithKind("InferencePoolImport")
+	b.WithAPIVersion("inference.networking.x-k8s.io/v1alpha1")
+	return b
+}
+func (b InferencePoolImportApplyConfiguration) IsApplyConfiguration() {}
+
+// WithKind sets the Kind field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Kind field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithKind(value string) *InferencePoolImportApplyConfiguration {
+	b.TypeMetaApplyConfiguration.Kind = &value
+	return b
+}
+
+// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the APIVersion field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithAPIVersion(value string) *InferencePoolImportApplyConfiguration {
+	b.TypeMetaApplyConfiguration.APIVersion = &value
+	return b
+}
+
+// WithName sets the Name field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Name field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithName(value string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.Name = &value
+	return b
+}
+
+// WithGenerateName sets the GenerateName field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the GenerateName field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithGenerateName(value string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.GenerateName = &value
+	return b
+}
+
+// WithNamespace sets the Namespace field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Namespace field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithNamespace(value string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.Namespace = &value
+	return b
+}
+
+// WithUID sets the UID field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the UID field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithUID(value types.UID) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.UID = &value
+	return b
+}
+
+// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ResourceVersion field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithResourceVersion(value string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.ResourceVersion = &value
+	return b
+}
+
+// WithGeneration sets the Generation field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Generation field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithGeneration(value int64) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.Generation = &value
+	return b
+}
+
+// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the CreationTimestamp field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithCreationTimestamp(value metav1.Time) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.CreationTimestamp = &value
+	return b
+}
+
+// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the DeletionTimestamp field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value
+	return b
+}
+
+// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value
+	return b
+}
+
+// WithLabels puts the entries into the Labels field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, the entries provided by each call will be put on the Labels field,
+// overwriting an existing map entries in Labels field with the same key.
+func (b *InferencePoolImportApplyConfiguration) WithLabels(entries map[string]string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 {
+		b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries))
+	}
+	for k, v := range entries {
+		b.ObjectMetaApplyConfiguration.Labels[k] = v
+	}
+	return b
+}
+
+// WithAnnotations puts the entries into the Annotations field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, the entries provided by each call will be put on the Annotations field,
+// overwriting an existing map entries in Annotations field with the same key.
+func (b *InferencePoolImportApplyConfiguration) WithAnnotations(entries map[string]string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 {
+		b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries))
+	}
+	for k, v := range entries {
+		b.ObjectMetaApplyConfiguration.Annotations[k] = v
+	}
+	return b
+}
+
+// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the OwnerReferences field.
+func (b *InferencePoolImportApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithOwnerReferences")
+		}
+		b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i])
+	}
+	return b
+}
+
+// WithFinalizers adds the given value to the Finalizers field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the Finalizers field.
+func (b *InferencePoolImportApplyConfiguration) WithFinalizers(values ...string) *InferencePoolImportApplyConfiguration {
+	b.ensureObjectMetaApplyConfigurationExists()
+	for i := range values {
+		b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i])
+	}
+	return b
+}
+
+func (b *InferencePoolImportApplyConfiguration) ensureObjectMetaApplyConfigurationExists() {
+	if b.ObjectMetaApplyConfiguration == nil {
+		b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{}
+	}
+}
+
+// WithStatus sets the Status field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Status field is set to the value of the last call.
+func (b *InferencePoolImportApplyConfiguration) WithStatus(value *InferencePoolImportStatusApplyConfiguration) *InferencePoolImportApplyConfiguration {
+	b.Status = value
+	return b
+}
+
+// GetKind retrieves the value of the Kind field in the declarative configuration.
+func (b *InferencePoolImportApplyConfiguration) GetKind() *string {
+	return b.TypeMetaApplyConfiguration.Kind
+}
+
+// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration.
+func (b *InferencePoolImportApplyConfiguration) GetAPIVersion() *string {
+	return b.TypeMetaApplyConfiguration.APIVersion
+}
+
+// GetName retrieves the value of the Name field in the declarative configuration.
+func (b *InferencePoolImportApplyConfiguration) GetName() *string {
+	b.ensureObjectMetaApplyConfigurationExists()
+	return b.ObjectMetaApplyConfiguration.Name
+}
+
+// GetNamespace retrieves the value of the Namespace field in the declarative configuration.
+func (b *InferencePoolImportApplyConfiguration) GetNamespace() *string {
+	b.ensureObjectMetaApplyConfigurationExists()
+	return b.ObjectMetaApplyConfiguration.Namespace
+}
diff --git a/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimportstatus.go b/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimportstatus.go
new file mode 100644
index 0000000..9c21414
--- /dev/null
+++ b/client-go/applyconfiguration/apix/v1alpha1/inferencepoolimportstatus.go
@@ -0,0 +1,44 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1alpha1
+
+// InferencePoolImportStatusApplyConfiguration represents a declarative configuration of the InferencePoolImportStatus type for use
+// with apply.
+type InferencePoolImportStatusApplyConfiguration struct {
+	Controllers []ImportControllerApplyConfiguration `json:"controllers,omitempty"`
+}
+
+// InferencePoolImportStatusApplyConfiguration constructs a declarative configuration of the InferencePoolImportStatus type for use with
+// apply.
+func InferencePoolImportStatus() *InferencePoolImportStatusApplyConfiguration {
+	return &InferencePoolImportStatusApplyConfiguration{}
+}
+
+// WithControllers adds the given value to the Controllers field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the Controllers field.
+func (b *InferencePoolImportStatusApplyConfiguration) WithControllers(values ...*ImportControllerApplyConfiguration) *InferencePoolImportStatusApplyConfiguration {
+	for i := range values {
+		if values[i] == nil {
+			panic("nil value passed to WithControllers")
+		}
+		b.Controllers = append(b.Controllers, *values[i])
+	}
+	return b
+}
diff --git a/client-go/applyconfiguration/utils.go b/client-go/applyconfiguration/utils.go
index 8e11c00..7e4ea09 100644
--- a/client-go/applyconfiguration/utils.go
+++ b/client-go/applyconfiguration/utils.go
@@ -23,8 +23,10 @@ import (
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	managedfields "k8s.io/apimachinery/pkg/util/managedfields"
 	v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
+	v1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
 	v1alpha2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2"
 	apiv1 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/api/v1"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/apix/v1alpha1"
 	apixv1alpha2 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/apix/v1alpha2"
 	internal "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/internal"
 )
@@ -51,6 +53,16 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
 	case v1.SchemeGroupVersion.WithKind("Port"):
 		return &apiv1.PortApplyConfiguration{}
 
+		// Group=inference.networking.x-k8s.io, Version=v1alpha1
+	case v1alpha1.SchemeGroupVersion.WithKind("ExportingCluster"):
+		return &apixv1alpha1.ExportingClusterApplyConfiguration{}
+	case v1alpha1.SchemeGroupVersion.WithKind("ImportController"):
+		return &apixv1alpha1.ImportControllerApplyConfiguration{}
+	case v1alpha1.SchemeGroupVersion.WithKind("InferencePoolImport"):
+		return &apixv1alpha1.InferencePoolImportApplyConfiguration{}
+	case v1alpha1.SchemeGroupVersion.WithKind("InferencePoolImportStatus"):
+		return &apixv1alpha1.InferencePoolImportStatusApplyConfiguration{}
+
 		// Group=inference.networking.x-k8s.io, Version=v1alpha2
 	case v1alpha2.SchemeGroupVersion.WithKind("Extension"):
 		return &apixv1alpha2.ExtensionApplyConfiguration{}
diff --git a/client-go/clientset/versioned/clientset.go b/client-go/clientset/versioned/clientset.go
index 928ab89..0ea4a00 100644
--- a/client-go/clientset/versioned/clientset.go
+++ b/client-go/clientset/versioned/clientset.go
@@ -26,12 +26,14 @@ import (
 	rest "k8s.io/client-go/rest"
 	flowcontrol "k8s.io/client-go/util/flowcontrol"
 	inferencev1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/api/v1"
+	inferencev1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha1"
 	xinferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha2"
 )
 
 type Interface interface {
 	Discovery() discovery.DiscoveryInterface
 	InferenceV1() inferencev1.InferenceV1Interface
+	InferenceV1alpha1() inferencev1alpha1.InferenceV1alpha1Interface
 	XInferenceV1alpha2() xinferencev1alpha2.XInferenceV1alpha2Interface
 }
 
@@ -39,6 +41,7 @@ type Interface interface {
 type Clientset struct {
 	*discovery.DiscoveryClient
 	inferenceV1        *inferencev1.InferenceV1Client
+	inferenceV1alpha1  *inferencev1alpha1.InferenceV1alpha1Client
 	xInferenceV1alpha2 *xinferencev1alpha2.XInferenceV1alpha2Client
 }
 
@@ -47,6 +50,11 @@ func (c *Clientset) InferenceV1() inferencev1.InferenceV1Interface {
 	return c.inferenceV1
 }
 
+// InferenceV1alpha1 retrieves the InferenceV1alpha1Client
+func (c *Clientset) InferenceV1alpha1() inferencev1alpha1.InferenceV1alpha1Interface {
+	return c.inferenceV1alpha1
+}
+
 // XInferenceV1alpha2 retrieves the XInferenceV1alpha2Client
 func (c *Clientset) XInferenceV1alpha2() xinferencev1alpha2.XInferenceV1alpha2Interface {
 	return c.xInferenceV1alpha2
@@ -100,6 +108,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset,
 	if err != nil {
 		return nil, err
 	}
+	cs.inferenceV1alpha1, err = inferencev1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
+	if err != nil {
+		return nil, err
+	}
 	cs.xInferenceV1alpha2, err = xinferencev1alpha2.NewForConfigAndClient(&configShallowCopy, httpClient)
 	if err != nil {
 		return nil, err
@@ -126,6 +138,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
 func New(c rest.Interface) *Clientset {
 	var cs Clientset
 	cs.inferenceV1 = inferencev1.New(c)
+	cs.inferenceV1alpha1 = inferencev1alpha1.New(c)
 	cs.xInferenceV1alpha2 = xinferencev1alpha2.New(c)
 
 	cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
diff --git a/client-go/clientset/versioned/fake/clientset_generated.go b/client-go/clientset/versioned/fake/clientset_generated.go
index a0709d7..baa42d8 100644
--- a/client-go/clientset/versioned/fake/clientset_generated.go
+++ b/client-go/clientset/versioned/fake/clientset_generated.go
@@ -29,6 +29,8 @@ import (
 	clientset "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned"
 	inferencev1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/api/v1"
 	fakeinferencev1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/api/v1/fake"
+	inferencev1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha1"
+	fakeinferencev1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha1/fake"
 	xinferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha2"
 	fakexinferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha2/fake"
 )
@@ -132,6 +134,11 @@ func (c *Clientset) InferenceV1() inferencev1.InferenceV1Interface {
 	return &fakeinferencev1.FakeInferenceV1{Fake: &c.Fake}
 }
 
+// InferenceV1alpha1 retrieves the InferenceV1alpha1Client
+func (c *Clientset) InferenceV1alpha1() inferencev1alpha1.InferenceV1alpha1Interface {
+	return &fakeinferencev1alpha1.FakeInferenceV1alpha1{Fake: &c.Fake}
+}
+
 // XInferenceV1alpha2 retrieves the XInferenceV1alpha2Client
 func (c *Clientset) XInferenceV1alpha2() xinferencev1alpha2.XInferenceV1alpha2Interface {
 	return &fakexinferencev1alpha2.FakeXInferenceV1alpha2{Fake: &c.Fake}
diff --git a/client-go/clientset/versioned/fake/register.go b/client-go/clientset/versioned/fake/register.go
index 5c6d338..412308d 100644
--- a/client-go/clientset/versioned/fake/register.go
+++ b/client-go/clientset/versioned/fake/register.go
@@ -25,6 +25,7 @@ import (
 	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
+	inferencev1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
 	xinferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2"
 )
 
@@ -33,6 +34,7 @@ var codecs = serializer.NewCodecFactory(scheme)
 
 var localSchemeBuilder = runtime.SchemeBuilder{
 	inferencev1.AddToScheme,
+	inferencev1alpha1.AddToScheme,
 	xinferencev1alpha2.AddToScheme,
 }
 
diff --git a/client-go/clientset/versioned/scheme/register.go b/client-go/clientset/versioned/scheme/register.go
index 7836df4..47bdc7c 100644
--- a/client-go/clientset/versioned/scheme/register.go
+++ b/client-go/clientset/versioned/scheme/register.go
@@ -25,6 +25,7 @@ import (
 	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
+	inferencev1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
 	xinferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2"
 )
 
@@ -33,6 +34,7 @@ var Codecs = serializer.NewCodecFactory(Scheme)
 var ParameterCodec = runtime.NewParameterCodec(Scheme)
 var localSchemeBuilder = runtime.SchemeBuilder{
 	inferencev1.AddToScheme,
+	inferencev1alpha1.AddToScheme,
 	xinferencev1alpha2.AddToScheme,
 }
 
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/apix_client.go b/client-go/clientset/versioned/typed/apix/v1alpha1/apix_client.go
new file mode 100644
index 0000000..726c410
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/apix_client.go
@@ -0,0 +1,101 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	http "net/http"
+
+	rest "k8s.io/client-go/rest"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+	scheme "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/scheme"
+)
+
+type InferenceV1alpha1Interface interface {
+	RESTClient() rest.Interface
+	InferencePoolImportsGetter
+}
+
+// InferenceV1alpha1Client is used to interact with features provided by the inference.networking.x-k8s.io group.
+type InferenceV1alpha1Client struct {
+	restClient rest.Interface
+}
+
+func (c *InferenceV1alpha1Client) InferencePoolImports(namespace string) InferencePoolImportInterface {
+	return newInferencePoolImports(c, namespace)
+}
+
+// NewForConfig creates a new InferenceV1alpha1Client for the given config.
+// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
+// where httpClient was generated with rest.HTTPClientFor(c).
+func NewForConfig(c *rest.Config) (*InferenceV1alpha1Client, error) {
+	config := *c
+	setConfigDefaults(&config)
+	httpClient, err := rest.HTTPClientFor(&config)
+	if err != nil {
+		return nil, err
+	}
+	return NewForConfigAndClient(&config, httpClient)
+}
+
+// NewForConfigAndClient creates a new InferenceV1alpha1Client for the given config and http client.
+// Note the http client provided takes precedence over the configured transport values.
+func NewForConfigAndClient(c *rest.Config, h *http.Client) (*InferenceV1alpha1Client, error) {
+	config := *c
+	setConfigDefaults(&config)
+	client, err := rest.RESTClientForConfigAndClient(&config, h)
+	if err != nil {
+		return nil, err
+	}
+	return &InferenceV1alpha1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new InferenceV1alpha1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *InferenceV1alpha1Client {
+	client, err := NewForConfig(c)
+	if err != nil {
+		panic(err)
+	}
+	return client
+}
+
+// New creates a new InferenceV1alpha1Client for the given RESTClient.
+func New(c rest.Interface) *InferenceV1alpha1Client {
+	return &InferenceV1alpha1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) {
+	gv := apixv1alpha1.SchemeGroupVersion
+	config.GroupVersion = &gv
+	config.APIPath = "/apis"
+	config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion()
+
+	if config.UserAgent == "" {
+		config.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *InferenceV1alpha1Client) RESTClient() rest.Interface {
+	if c == nil {
+		return nil
+	}
+	return c.restClient
+}
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/doc.go b/client-go/clientset/versioned/typed/apix/v1alpha1/doc.go
new file mode 100644
index 0000000..df51baa
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1alpha1
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/fake/doc.go b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/doc.go
new file mode 100644
index 0000000..16f4439
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// Package fake has the automatically generated clients.
+package fake
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_apix_client.go b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_apix_client.go
new file mode 100644
index 0000000..1de7688
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_apix_client.go
@@ -0,0 +1,40 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	rest "k8s.io/client-go/rest"
+	testing "k8s.io/client-go/testing"
+	v1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha1"
+)
+
+type FakeInferenceV1alpha1 struct {
+	*testing.Fake
+}
+
+func (c *FakeInferenceV1alpha1) InferencePoolImports(namespace string) v1alpha1.InferencePoolImportInterface {
+	return newFakeInferencePoolImports(c, namespace)
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeInferenceV1alpha1) RESTClient() rest.Interface {
+	var ret *rest.RESTClient
+	return ret
+}
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_inferencepoolimport.go b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_inferencepoolimport.go
new file mode 100644
index 0000000..7a6938f
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/fake/fake_inferencepoolimport.go
@@ -0,0 +1,53 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	gentype "k8s.io/client-go/gentype"
+	v1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/apix/v1alpha1"
+	typedapixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/typed/apix/v1alpha1"
+)
+
+// fakeInferencePoolImports implements InferencePoolImportInterface
+type fakeInferencePoolImports struct {
+	*gentype.FakeClientWithListAndApply[*v1alpha1.InferencePoolImport, *v1alpha1.InferencePoolImportList, *apixv1alpha1.InferencePoolImportApplyConfiguration]
+	Fake *FakeInferenceV1alpha1
+}
+
+func newFakeInferencePoolImports(fake *FakeInferenceV1alpha1, namespace string) typedapixv1alpha1.InferencePoolImportInterface {
+	return &fakeInferencePoolImports{
+		gentype.NewFakeClientWithListAndApply[*v1alpha1.InferencePoolImport, *v1alpha1.InferencePoolImportList, *apixv1alpha1.InferencePoolImportApplyConfiguration](
+			fake.Fake,
+			namespace,
+			v1alpha1.SchemeGroupVersion.WithResource("inferencepoolimports"),
+			v1alpha1.SchemeGroupVersion.WithKind("InferencePoolImport"),
+			func() *v1alpha1.InferencePoolImport { return &v1alpha1.InferencePoolImport{} },
+			func() *v1alpha1.InferencePoolImportList { return &v1alpha1.InferencePoolImportList{} },
+			func(dst, src *v1alpha1.InferencePoolImportList) { dst.ListMeta = src.ListMeta },
+			func(list *v1alpha1.InferencePoolImportList) []*v1alpha1.InferencePoolImport {
+				return gentype.ToPointerSlice(list.Items)
+			},
+			func(list *v1alpha1.InferencePoolImportList, items []*v1alpha1.InferencePoolImport) {
+				list.Items = gentype.FromPointerSlice(items)
+			},
+		),
+		fake,
+	}
+}
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/generated_expansion.go b/client-go/clientset/versioned/typed/apix/v1alpha1/generated_expansion.go
new file mode 100644
index 0000000..0969f5b
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+type InferencePoolImportExpansion interface{}
diff --git a/client-go/clientset/versioned/typed/apix/v1alpha1/inferencepoolimport.go b/client-go/clientset/versioned/typed/apix/v1alpha1/inferencepoolimport.go
new file mode 100644
index 0000000..f68766d
--- /dev/null
+++ b/client-go/clientset/versioned/typed/apix/v1alpha1/inferencepoolimport.go
@@ -0,0 +1,74 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	context "context"
+
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	gentype "k8s.io/client-go/gentype"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+	applyconfigurationapixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/applyconfiguration/apix/v1alpha1"
+	scheme "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned/scheme"
+)
+
+// InferencePoolImportsGetter has a method to return a InferencePoolImportInterface.
+// A group's client should implement this interface.
+type InferencePoolImportsGetter interface {
+	InferencePoolImports(namespace string) InferencePoolImportInterface
+}
+
+// InferencePoolImportInterface has methods to work with InferencePoolImport resources.
+type InferencePoolImportInterface interface {
+	Create(ctx context.Context, inferencePoolImport *apixv1alpha1.InferencePoolImport, opts v1.CreateOptions) (*apixv1alpha1.InferencePoolImport, error)
+	Update(ctx context.Context, inferencePoolImport *apixv1alpha1.InferencePoolImport, opts v1.UpdateOptions) (*apixv1alpha1.InferencePoolImport, error)
+	// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+	UpdateStatus(ctx context.Context, inferencePoolImport *apixv1alpha1.InferencePoolImport, opts v1.UpdateOptions) (*apixv1alpha1.InferencePoolImport, error)
+	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+	Get(ctx context.Context, name string, opts v1.GetOptions) (*apixv1alpha1.InferencePoolImport, error)
+	List(ctx context.Context, opts v1.ListOptions) (*apixv1alpha1.InferencePoolImportList, error)
+	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apixv1alpha1.InferencePoolImport, err error)
+	Apply(ctx context.Context, inferencePoolImport *applyconfigurationapixv1alpha1.InferencePoolImportApplyConfiguration, opts v1.ApplyOptions) (result *apixv1alpha1.InferencePoolImport, err error)
+	// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
+	ApplyStatus(ctx context.Context, inferencePoolImport *applyconfigurationapixv1alpha1.InferencePoolImportApplyConfiguration, opts v1.ApplyOptions) (result *apixv1alpha1.InferencePoolImport, err error)
+	InferencePoolImportExpansion
+}
+
+// inferencePoolImports implements InferencePoolImportInterface
+type inferencePoolImports struct {
+	*gentype.ClientWithListAndApply[*apixv1alpha1.InferencePoolImport, *apixv1alpha1.InferencePoolImportList, *applyconfigurationapixv1alpha1.InferencePoolImportApplyConfiguration]
+}
+
+// newInferencePoolImports returns a InferencePoolImports
+func newInferencePoolImports(c *InferenceV1alpha1Client, namespace string) *inferencePoolImports {
+	return &inferencePoolImports{
+		gentype.NewClientWithListAndApply[*apixv1alpha1.InferencePoolImport, *apixv1alpha1.InferencePoolImportList, *applyconfigurationapixv1alpha1.InferencePoolImportApplyConfiguration](
+			"inferencepoolimports",
+			c.RESTClient(),
+			scheme.ParameterCodec,
+			namespace,
+			func() *apixv1alpha1.InferencePoolImport { return &apixv1alpha1.InferencePoolImport{} },
+			func() *apixv1alpha1.InferencePoolImportList { return &apixv1alpha1.InferencePoolImportList{} },
+		),
+	}
+}
diff --git a/client-go/informers/externalversions/apix/interface.go b/client-go/informers/externalversions/apix/interface.go
index 26e1817..426cbcd 100644
--- a/client-go/informers/externalversions/apix/interface.go
+++ b/client-go/informers/externalversions/apix/interface.go
@@ -19,12 +19,15 @@ limitations under the License.
 package apix
 
 import (
+	v1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/informers/externalversions/apix/v1alpha1"
 	v1alpha2 "sigs.k8s.io/gateway-api-inference-extension/client-go/informers/externalversions/apix/v1alpha2"
 	internalinterfaces "sigs.k8s.io/gateway-api-inference-extension/client-go/informers/externalversions/internalinterfaces"
 )
 
 // Interface provides access to each of this group's versions.
 type Interface interface {
+	// V1alpha1 provides access to shared informers for resources in V1alpha1.
+	V1alpha1() v1alpha1.Interface
 	// V1alpha2 provides access to shared informers for resources in V1alpha2.
 	V1alpha2() v1alpha2.Interface
 }
@@ -40,6 +43,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
 	return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
 }
 
+// V1alpha1 returns a new v1alpha1.Interface.
+func (g *group) V1alpha1() v1alpha1.Interface {
+	return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
+}
+
 // V1alpha2 returns a new v1alpha2.Interface.
 func (g *group) V1alpha2() v1alpha2.Interface {
 	return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions)
diff --git a/client-go/informers/externalversions/apix/v1alpha1/inferencepoolimport.go b/client-go/informers/externalversions/apix/v1alpha1/inferencepoolimport.go
new file mode 100644
index 0000000..9013e02
--- /dev/null
+++ b/client-go/informers/externalversions/apix/v1alpha1/inferencepoolimport.go
@@ -0,0 +1,102 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	context "context"
+	time "time"
+
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	watch "k8s.io/apimachinery/pkg/watch"
+	cache "k8s.io/client-go/tools/cache"
+	gatewayapiinferenceextensionapixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+	versioned "sigs.k8s.io/gateway-api-inference-extension/client-go/clientset/versioned"
+	internalinterfaces "sigs.k8s.io/gateway-api-inference-extension/client-go/informers/externalversions/internalinterfaces"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/client-go/listers/apix/v1alpha1"
+)
+
+// InferencePoolImportInformer provides access to a shared informer and lister for
+// InferencePoolImports.
+type InferencePoolImportInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() apixv1alpha1.InferencePoolImportLister
+}
+
+type inferencePoolImportInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	namespace        string
+}
+
+// NewInferencePoolImportInformer constructs a new informer for InferencePoolImport type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewInferencePoolImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredInferencePoolImportInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredInferencePoolImportInformer constructs a new informer for InferencePoolImport type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredInferencePoolImportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+	return cache.NewSharedIndexInformer(
+		&cache.ListWatch{
+			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.InferenceV1alpha1().InferencePoolImports(namespace).List(context.Background(), options)
+			},
+			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.InferenceV1alpha1().InferencePoolImports(namespace).Watch(context.Background(), options)
+			},
+			ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.InferenceV1alpha1().InferencePoolImports(namespace).List(ctx, options)
+			},
+			WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.InferenceV1alpha1().InferencePoolImports(namespace).Watch(ctx, options)
+			},
+		},
+		&gatewayapiinferenceextensionapixv1alpha1.InferencePoolImport{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *inferencePoolImportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredInferencePoolImportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *inferencePoolImportInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&gatewayapiinferenceextensionapixv1alpha1.InferencePoolImport{}, f.defaultInformer)
+}
+
+func (f *inferencePoolImportInformer) Lister() apixv1alpha1.InferencePoolImportLister {
+	return apixv1alpha1.NewInferencePoolImportLister(f.Informer().GetIndexer())
+}
diff --git a/client-go/informers/externalversions/apix/v1alpha1/interface.go b/client-go/informers/externalversions/apix/v1alpha1/interface.go
new file mode 100644
index 0000000..50e84d5
--- /dev/null
+++ b/client-go/informers/externalversions/apix/v1alpha1/interface.go
@@ -0,0 +1,45 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	internalinterfaces "sigs.k8s.io/gateway-api-inference-extension/client-go/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+	// InferencePoolImports returns a InferencePoolImportInformer.
+	InferencePoolImports() InferencePoolImportInformer
+}
+
+type version struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// InferencePoolImports returns a InferencePoolImportInformer.
+func (v *version) InferencePoolImports() InferencePoolImportInformer {
+	return &inferencePoolImportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/client-go/informers/externalversions/generic.go b/client-go/informers/externalversions/generic.go
index 03c95ee..2fe2915 100644
--- a/client-go/informers/externalversions/generic.go
+++ b/client-go/informers/externalversions/generic.go
@@ -24,6 +24,7 @@ import (
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	cache "k8s.io/client-go/tools/cache"
 	v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
+	v1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
 	v1alpha2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2"
 )
 
@@ -57,6 +58,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
 	case v1.SchemeGroupVersion.WithResource("inferencepools"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Inference().V1().InferencePools().Informer()}, nil
 
+		// Group=inference.networking.x-k8s.io, Version=v1alpha1
+	case v1alpha1.SchemeGroupVersion.WithResource("inferencepoolimports"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.XInference().V1alpha1().InferencePoolImports().Informer()}, nil
+
 		// Group=inference.networking.x-k8s.io, Version=v1alpha2
 	case v1alpha2.SchemeGroupVersion.WithResource("inferenceobjectives"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.XInference().V1alpha2().InferenceObjectives().Informer()}, nil
diff --git a/client-go/listers/apix/v1alpha1/expansion_generated.go b/client-go/listers/apix/v1alpha1/expansion_generated.go
new file mode 100644
index 0000000..7c0d32c
--- /dev/null
+++ b/client-go/listers/apix/v1alpha1/expansion_generated.go
@@ -0,0 +1,27 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+// InferencePoolImportListerExpansion allows custom methods to be added to
+// InferencePoolImportLister.
+type InferencePoolImportListerExpansion interface{}
+
+// InferencePoolImportNamespaceListerExpansion allows custom methods to be added to
+// InferencePoolImportNamespaceLister.
+type InferencePoolImportNamespaceListerExpansion interface{}
diff --git a/client-go/listers/apix/v1alpha1/inferencepoolimport.go b/client-go/listers/apix/v1alpha1/inferencepoolimport.go
new file mode 100644
index 0000000..653bbfa
--- /dev/null
+++ b/client-go/listers/apix/v1alpha1/inferencepoolimport.go
@@ -0,0 +1,70 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	labels "k8s.io/apimachinery/pkg/labels"
+	listers "k8s.io/client-go/listers"
+	cache "k8s.io/client-go/tools/cache"
+	apixv1alpha1 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1"
+)
+
+// InferencePoolImportLister helps list InferencePoolImports.
+// All objects returned here must be treated as read-only.
+type InferencePoolImportLister interface {
+	// List lists all InferencePoolImports in the indexer.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*apixv1alpha1.InferencePoolImport, err error)
+	// InferencePoolImports returns an object that can list and get InferencePoolImports.
+	InferencePoolImports(namespace string) InferencePoolImportNamespaceLister
+	InferencePoolImportListerExpansion
+}
+
+// inferencePoolImportLister implements the InferencePoolImportLister interface.
+type inferencePoolImportLister struct {
+	listers.ResourceIndexer[*apixv1alpha1.InferencePoolImport]
+}
+
+// NewInferencePoolImportLister returns a new InferencePoolImportLister.
+func NewInferencePoolImportLister(indexer cache.Indexer) InferencePoolImportLister {
+	return &inferencePoolImportLister{listers.New[*apixv1alpha1.InferencePoolImport](indexer, apixv1alpha1.Resource("inferencepoolimport"))}
+}
+
+// InferencePoolImports returns an object that can list and get InferencePoolImports.
+func (s *inferencePoolImportLister) InferencePoolImports(namespace string) InferencePoolImportNamespaceLister {
+	return inferencePoolImportNamespaceLister{listers.NewNamespaced[*apixv1alpha1.InferencePoolImport](s.ResourceIndexer, namespace)}
+}
+
+// InferencePoolImportNamespaceLister helps list and get InferencePoolImports.
+// All objects returned here must be treated as read-only.
+type InferencePoolImportNamespaceLister interface {
+	// List lists all InferencePoolImports in the indexer for a given namespace.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*apixv1alpha1.InferencePoolImport, err error)
+	// Get retrieves the InferencePoolImport from the indexer for a given namespace and name.
+	// Objects returned here must be treated as read-only.
+	Get(name string) (*apixv1alpha1.InferencePoolImport, error)
+	InferencePoolImportNamespaceListerExpansion
+}
+
+// inferencePoolImportNamespaceLister implements the InferencePoolImportNamespaceLister
+// interface.
+type inferencePoolImportNamespaceLister struct {
+	listers.ResourceIndexer[*apixv1alpha1.InferencePoolImport]
+}
diff --git a/cmd/epp/runner/runner.go b/cmd/epp/runner/runner.go
index 2842704..06293c7 100644
--- a/cmd/epp/runner/runner.go
+++ b/cmd/epp/runner/runner.go
@@ -50,6 +50,9 @@ import (
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
 	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"
+	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"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/metrics/collectors"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
@@ -68,11 +71,25 @@ import (
 )
 
 const (
-	// enableExperimentalDatalayerV2 defines the environment variable
-	// used as feature flag for the pluggable data layer.
+	// enableExperimentalDatalayerV2 defines the environment variable used as feature flag for the pluggable data layer.
 	enableExperimentalDatalayerV2 = "ENABLE_EXPERIMENTAL_DATALAYER_V2"
+	// enableExperimentalFlowControlLayer defines the environment variable used as a feature flag for the pluggable flow
+	// control layer.
+	enableExperimentalFlowControlLayer = "ENABLE_EXPERIMENTAL_FLOW_CONTROL_LAYER"
 )
 
+// TODO: this is hardcoded for POC only. This needs to be hooked up to our text-based config story.
+var flowControlConfig = flowcontrol.Config{
+	Controller: fccontroller.Config{}, // Use all defaults.
+	Registry: fcregistry.Config{
+		// Define domain of accepted priority levels as this field is required. Use defaults for all optional fields.
+		// TODO: this should not be hardcoded.
+		PriorityBands: []fcregistry.PriorityBandConfig{
+			{Priority: 0, PriorityName: "Default"},
+		},
+	},
+}
+
 var (
 	grpcPort       = flag.Int("grpc-port", runserver.DefaultGrpcPort, "The gRPC port used for communicating with Envoy proxy")
 	grpcHealthPort = flag.Int("grpc-health-port", runserver.DefaultGrpcHealthPort, "The port used for gRPC liveness and readiness probes")
@@ -106,6 +123,7 @@ var (
 	modelServerMetricsScheme                  = flag.String("model-server-metrics-scheme", "http", "Scheme to scrape metrics from pods")
 	modelServerMetricsHttpsInsecureSkipVerify = flag.Bool("model-server-metrics-https-insecure-skip-verify", true, "When using 'https' scheme for 'model-server-metrics-scheme', configure 'InsecureSkipVerify' (default to true)")
 	haEnableLeaderElection                    = flag.Bool("ha-enable-leader-election", false, "Enables leader election for high availability. When enabled, readiness probes will only pass on the leader.")
+	tracing                                   = flag.Bool("tracing", true, "Enables emitting traces")
 
 	setupLog = ctrl.Log.WithName("setup")
 )
@@ -141,6 +159,13 @@ func (r *Runner) Run(ctx context.Context) error {
 	flag.Parse()
 	initLogging(&opts)
 
+	if *tracing {
+		err := common.InitTracing(ctx, setupLog)
+		if err != nil {
+			return err
+		}
+	}
+
 	setupLog.Info("GIE build", "commit-sha", version.CommitSHA, "build-ref", version.BuildRef)
 
 	// Validate flags
@@ -244,7 +269,7 @@ func (r *Runner) Run(ctx context.Context) error {
 		runtime.SetBlockProfileRate(1)
 	}
 
-	err = r.parsePluginsConfiguration(ctx)
+	err = r.parsePluginsConfiguration(ctx, datastore)
 	if err != nil {
 		setupLog.Error(err, "Failed to parse plugins configuration")
 		return err
@@ -263,7 +288,43 @@ func (r *Runner) Run(ctx context.Context) error {
 
 	saturationDetector := saturationdetector.NewDetector(sdConfig, setupLog)
 
-	director := requestcontrol.NewDirectorWithConfig(datastore, scheduler, saturationDetector, r.requestControlConfig)
+	// --- Admission Control Initialization ---
+	enableFlowControl := env.GetEnvBool(enableExperimentalFlowControlLayer, false, setupLog)
+	var admissionController requestcontrol.AdmissionController
+	if enableFlowControl {
+		setupLog.Info("Initializing experimental Flow Control layer")
+		fcCfg, err := flowControlConfig.ValidateAndApplyDefaults()
+		if err != nil {
+			setupLog.Error(err, "failed to initialize Flow Control layer")
+			return fmt.Errorf("invalid Flow Control config: %w", err)
+		}
+
+		registry, err := fcregistry.NewFlowRegistry(fcCfg.Registry, setupLog)
+		if err != nil {
+			return fmt.Errorf("failed to initialize Flow Registry: %w", err)
+		}
+		fc, err := fccontroller.NewFlowController(
+			ctx,
+			fcCfg.Controller,
+			registry,
+			saturationDetector,
+			setupLog,
+		)
+		if err != nil {
+			return fmt.Errorf("failed to initialize Flow Controller: %w", err)
+		}
+		go registry.Run(ctx)
+		admissionController = requestcontrol.NewFlowControlAdmissionController(saturationDetector, fc)
+	} else {
+		setupLog.Info("Experimental Flow Control layer is disabled, using legacy admission control")
+		admissionController = requestcontrol.NewLegacyAdmissionController(saturationDetector)
+	}
+
+	director := requestcontrol.NewDirectorWithConfig(
+		datastore,
+		scheduler,
+		admissionController,
+		r.requestControlConfig)
 
 	// --- Setup ExtProc Server Runner ---
 	serverRunner := &runserver.ExtProcServerRunner{
@@ -321,7 +382,7 @@ func (r *Runner) registerInTreePlugins() {
 	plugins.Register(testfilter.HeaderBasedTestingFilterType, testfilter.HeaderBasedTestingFilterFactory)
 }
 
-func (r *Runner) parsePluginsConfiguration(ctx context.Context) error {
+func (r *Runner) parsePluginsConfiguration(ctx context.Context, ds datastore.Datastore) error {
 	if *configText == "" && *configFile == "" {
 		return nil // configuring through code, not through file
 	}
@@ -340,8 +401,9 @@ func (r *Runner) parsePluginsConfiguration(ctx context.Context) error {
 	}
 
 	r.registerInTreePlugins()
-	handle := plugins.NewEppHandle(ctx)
+	handle := plugins.NewEppHandle(ctx, ds.PodList)
 	config, err := loader.LoadConfig(configBytes, handle, logger)
+
 	if err != nil {
 		return fmt.Errorf("failed to load the configuration - %w", err)
 	}
diff --git a/config/charts/inferencepool/README.md b/config/charts/inferencepool/README.md
index 41fee83..f6354bf 100644
--- a/config/charts/inferencepool/README.md
+++ b/config/charts/inferencepool/README.md
@@ -166,30 +166,34 @@ $ helm uninstall pool-1
 
 The following table list the configurable parameters of the chart.
 
-| **Parameter Name**                          | **Description**                                                                                                        |
-|---------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
-| `inferencePool.apiVersion`                  | The API version of the InferencePool resource. Defaults to `inference.networking.k8s.io/v1`. This can be changed to `inference.networking.x-k8s.io/v1alpha2` to support older API versions. |
-| `inferencePool.targetPortNumber`            | Target port number for the vllm backends, will be used to scrape metrics by the inference extension. Defaults to 8000. |
-| `inferencePool.modelServerType`            | Type of the model servers in the pool, valid options are [vllm, triton-tensorrt-llm], default is vllm. |
-| `inferencePool.modelServers.matchLabels`    | Label selector to match vllm backends managed by the inference pool.                                                   |
-| `inferenceExtension.replicas`               | Number of replicas for the endpoint picker extension service. If More than one replica is used, EPP will run in HA active-passive mode. Defaults to `1`.                                         |
-| `inferenceExtension.image.name`             | Name of the container image used for the endpoint picker.                                                              |
-| `inferenceExtension.image.hub`              | Registry URL where the endpoint picker image is hosted.                                                                |
-| `inferenceExtension.image.tag`              | Image tag of the endpoint picker.                                                                                      |
-| `inferenceExtension.image.pullPolicy`       | Image pull policy for the container. Possible values: `Always`, `IfNotPresent`, or `Never`. Defaults to `Always`.      |
-| `inferenceExtension.env`                    | List of environment variables to set in the endpoint picker container as free-form YAML. Defaults to `[]`.             |
-| `inferenceExtension.extraContainerPorts`    | List of additional container ports to expose. Defaults to `[]`.                                                        |
-| `inferenceExtension.extraServicePorts`      | List of additional service ports to expose. Defaults to `[]`.                                                          |
-| `inferenceExtension.flags`                  | List of flags which are passed through to endpoint picker. Example flags, enable-pprof, grpc-port etc. Refer [runner.go](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/cmd/epp/runner/runner.go) for complete list.                                                            |
-| `inferenceExtension.affinity`               | Affinity for the endpoint picker. Defaults to `{}`.                                                                    |
-| `inferenceExtension.tolerations`            | Tolerations for the endpoint picker. Defaults to `[]`.                                                                 |   |
-| `inferenceExtension.monitoring.interval`   | Metrics scraping interval for monitoring. Defaults to `10s`.                                                           |
-| `inferenceExtension.monitoring.secret.name` | Name of the service account token secret for metrics authentication. Defaults to `inference-gateway-sa-metrics-reader-secret`. |
-| `inferenceExtension.monitoring.prometheus.enabled` | Enable Prometheus ServiceMonitor creation for EPP metrics collection. Defaults to `false`.                      |
-| `inferenceExtension.monitoring.gke.enabled` | Enable GKE monitoring resources (`PodMonitoring` and RBAC). Defaults to `false`. |
-| `inferenceExtension.pluginsCustomConfig`    | Custom config that is passed to EPP as inline yaml.      |
-| `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`. |
+| **Parameter Name**                                 | **Description**                                                                                                                                                                                                                                                                                                                       |
+|----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `inferencePool.apiVersion`                         | The API version of the InferencePool resource. Defaults to `inference.networking.k8s.io/v1`. This can be changed to `inference.networking.x-k8s.io/v1alpha2` to support older API versions.                                                                                                                                           |
+| `inferencePool.targetPortNumber`                   | Target port number for the vllm backends, will be used to scrape metrics by the inference extension. Defaults to 8000.                                                                                                                                                                                                                |
+| `inferencePool.modelServerType`                    | Type of the model servers in the pool, valid options are [vllm, triton-tensorrt-llm], default is vllm.                                                                                                                                                                                                                                |
+| `inferencePool.modelServers.matchLabels`           | Label selector to match vllm backends managed by the inference pool.                                                                                                                                                                                                                                                                  |
+| `inferenceExtension.replicas`                      | Number of replicas for the endpoint picker extension service. If More than one replica is used, EPP will run in HA active-passive mode. Defaults to `1`.                                                                                                                                                                              |
+| `inferenceExtension.image.name`                    | Name of the container image used for the endpoint picker.                                                                                                                                                                                                                                                                             |
+| `inferenceExtension.image.hub`                     | Registry URL where the endpoint picker image is hosted.                                                                                                                                                                                                                                                                               |
+| `inferenceExtension.image.tag`                     | Image tag of the endpoint picker.                                                                                                                                                                                                                                                                                                     |
+| `inferenceExtension.image.pullPolicy`              | Image pull policy for the container. Possible values: `Always`, `IfNotPresent`, or `Never`. Defaults to `Always`.                                                                                                                                                                                                                     |
+| `inferenceExtension.env`                           | List of environment variables to set in the endpoint picker container as free-form YAML. Defaults to `[]`.                                                                                                                                                                                                                            |
+| `inferenceExtension.extraContainerPorts`           | List of additional container ports to expose. Defaults to `[]`.                                                                                                                                                                                                                                                                       |
+| `inferenceExtension.extraServicePorts`             | List of additional service ports to expose. Defaults to `[]`.                                                                                                                                                                                                                                                                         |
+| `inferenceExtension.flags`                         | List of flags which are passed through to endpoint picker. Example flags, enable-pprof, grpc-port etc. Refer [runner.go](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/cmd/epp/runner/runner.go) for complete list.                                                                                                                                                                                      |
+| `inferenceExtension.affinity`                      | Affinity for the endpoint picker. Defaults to `{}`.                                                                                                                                                                                                                                                                                   |
+| `inferenceExtension.tolerations`                   | Tolerations for the endpoint picker. Defaults to `[]`.                                                                                                                                                                                                                                                                                |
+| `inferenceExtension.monitoring.interval`           | Metrics scraping interval for monitoring. Defaults to `10s`.                                                                                                                                                                                                                                                                          |
+| `inferenceExtension.monitoring.secret.name`        | Name of the service account token secret for metrics authentication. Defaults to `inference-gateway-sa-metrics-reader-secret`.                                                                                                                                                                                                        |
+| `inferenceExtension.monitoring.prometheus.enabled` | Enable Prometheus ServiceMonitor creation for EPP metrics collection. Defaults to `false`.                                                                                                                                                                                                                                            |
+| `inferenceExtension.monitoring.gke.enabled`        | Enable GKE monitoring resources (`PodMonitoring` and RBAC). Defaults to `false`.                                                                                                                                                                                                                                                      |
+| `inferenceExtension.pluginsCustomConfig`           | Custom config that is passed to EPP as inline yaml.                                                                                                                                                                                                                                                                                   |
+| `inferenceExtension.tracing.enabled`               | Enables or disables OpenTelemetry tracing globally for the EndpointPicker.                                                                                                                                                                                                                                                            |
+| `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.                                                                                                                       |
+| `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`.                                                                                                                                                                                                          |
 
 ### Provider Specific Configuration
 
@@ -214,6 +218,21 @@ These are the options available to you with `provider.name` set to `istio`:
 | `istio.destinationRule.host`            | Custom host value for the destination rule. If not set this will use the default value which is derrived from the epp service name and release namespace to gerenate a valid service address. |
 | `istio.destinationRule.trafficPolicy.connectionPool`            | Configure the connectionPool level settings of the traffic policy |
 
+#### OpenTelemetry
+
+The EndpointPicker supports OpenTelemetry-based tracing. To enable trace collection, use the following configuration:
+```yaml
+inferenceExtension:
+  tracing:
+    enabled: true
+    otelExporterEndpoint: "http://localhost:4317"
+    sampling:
+      sampler: "parentbased_traceidratio"
+      samplerArg: "0.1"
+```
+Make sure that the `otelExporterEndpoint` points to your OpenTelemetry collector endpoint. 
+Current only the `parentbased_traceidratio` sampler is supported. You can adjust the base sampling ratio using the `samplerArg` (e.g., 0.1 means 10% of traces will be sampled).
+
 ## Notes
 
 This chart will only deploy an InferencePool and its corresponding EndpointPicker extension. Before install the chart, please make sure that the inference extension CRDs are installed in the cluster. For more details, please refer to the [getting started guide](https://gateway-api-inference-extension.sigs.k8s.io/guides/).
diff --git a/config/charts/inferencepool/templates/epp-deployment.yaml b/config/charts/inferencepool/templates/epp-deployment.yaml
index f012c2e..1202402 100644
--- a/config/charts/inferencepool/templates/epp-deployment.yaml
+++ b/config/charts/inferencepool/templates/epp-deployment.yaml
@@ -62,6 +62,12 @@ spec:
         - "--{{ .name }}"
         - "{{ .value }}"
         {{- end }}
+        - "--tracing"
+        {{- if .Values.inferenceExtension.tracing.enabled }}
+        - "true"
+        {{- else }}
+        - "false"
+        {{- end }}
         ports:
         - name: grpc
           containerPort: 9002
@@ -101,6 +107,30 @@ spec:
           valueFrom:
             fieldRef:
               fieldPath: metadata.namespace
+        {{- if .Values.inferenceExtension.tracing.enabled }}
+        - name: OTEL_SERVICE_NAME
+          value: "gateway-api-inference-extension"
+        - name: OTEL_EXPORTER_OTLP_ENDPOINT
+          value: {{ .Values.inferenceExtension.tracing.otelExporterEndpoint | quote }}
+        - name: OTEL_TRACES_EXPORTER
+          value: "otlp"
+        - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: spec.nodeName
+        - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        - name: OTEL_RESOURCE_ATTRIBUTES
+          value: 'k8s.namespace.name=$(NAMESPACE),k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)'
+        - name: OTEL_TRACES_SAMPLER
+          value: {{ .Values.inferenceExtension.tracing.sampling.sampler | quote }}
+        - name: OTEL_TRACES_SAMPLER_ARG
+          value: {{ .Values.inferenceExtension.tracing.sampling.samplerArg | quote }}
+        {{- end }}
         {{- if .Values.inferenceExtension.env }}
         {{- toYaml .Values.inferenceExtension.env | nindent 8 }}
         {{- end }}
diff --git a/config/charts/inferencepool/values.yaml b/config/charts/inferencepool/values.yaml
index 91d6a48..f901f7f 100644
--- a/config/charts/inferencepool/values.yaml
+++ b/config/charts/inferencepool/values.yaml
@@ -53,6 +53,12 @@ inferenceExtension:
     
     gke:
       enabled: false
+  tracing:
+    enabled: false
+    otelExporterEndpoint: "http://localhost:4317"
+    sampling:
+      sampler: "parentbased_traceidratio"
+      samplerArg: "0.1"
 
 inferencePool:
   targetPorts:
@@ -85,4 +91,4 @@ istio:
     trafficPolicy: {}
       # connectionPool:
       #   http:
-      #     maxRequestsPerConnection: 256000
+      #     maxRequestsPerConnection: 256000
\ No newline at end of file
diff --git a/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml b/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
index 2ccaf23..a3f7696 100644
--- a/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
+++ b/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
@@ -147,6 +147,8 @@ spec:
                       MatchLabels contains a set of required {key,value} pairs.
                       An object must match every label in this map to be selected.
                       The matching logic is an AND operation on all entries.
+                    maxProperties: 64
+                    minProperties: 1
                     type: object
                 required:
                 - matchLabels
@@ -271,6 +273,25 @@ spec:
                       x-kubernetes-list-map-keys:
                       - type
                       x-kubernetes-list-type: map
+                    controllerName:
+                      description: |-
+                        ControllerName is a domain/path string that indicates the name of the controller that
+                        wrote this status. This corresponds with the GatewayClass controllerName field when the
+                        parentRef references a Gateway kind.
+
+                        Example: "example.net/gateway-controller".
+
+                        The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names:
+
+                         https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+
+                        Controllers MAY populate this field when writing status. When populating this field, controllers
+                        should ensure that entries to status populated with their ControllerName are cleaned up when they
+                        are no longer necessary.
+                      maxLength: 253
+                      minLength: 1
+                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$
+                      type: string
                     parentRef:
                       description: |-
                         ParentRef is used to identify the parent resource that this status
diff --git a/config/crd/bases/inference.networking.x-k8s.io_inferencepoolimports.yaml b/config/crd/bases/inference.networking.x-k8s.io_inferencepoolimports.yaml
new file mode 100644
index 0000000..318e6d4
--- /dev/null
+++ b/config/crd/bases/inference.networking.x-k8s.io_inferencepoolimports.yaml
@@ -0,0 +1,330 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    inference.networking.k8s.io/bundle-version: main-dev
+  name: inferencepoolimports.inference.networking.x-k8s.io
+spec:
+  group: inference.networking.x-k8s.io
+  names:
+    kind: InferencePoolImport
+    listKind: InferencePoolImportList
+    plural: inferencepoolimports
+    shortNames:
+    - infpimp
+    singular: inferencepoolimport
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: InferencePoolImport is the Schema for the InferencePoolImports
+          API.
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          status:
+            description: Status defines the observed state of the InferencePoolImport.
+            properties:
+              controllers:
+                description: Controllers is a list of controllers that are responsible
+                  for managing the InferencePoolImport.
+                items:
+                  description: ImportController defines a controller that is responsible
+                    for managing the InferencePoolImport.
+                  properties:
+                    conditions:
+                      description: |-
+                        Conditions track the state of the InferencePoolImport.
+
+                        Known condition types are:
+
+                         * "Accepted"
+                      items:
+                        description: Condition contains details for one aspect of
+                          the current state of this API Resource.
+                        properties:
+                          lastTransitionTime:
+                            description: |-
+                              lastTransitionTime is the last time the condition transitioned from one status to another.
+                              This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
+                            format: date-time
+                            type: string
+                          message:
+                            description: |-
+                              message is a human readable message indicating details about the transition.
+                              This may be an empty string.
+                            maxLength: 32768
+                            type: string
+                          observedGeneration:
+                            description: |-
+                              observedGeneration represents the .metadata.generation that the condition was set based upon.
+                              For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+                              with respect to the current state of the instance.
+                            format: int64
+                            minimum: 0
+                            type: integer
+                          reason:
+                            description: |-
+                              reason contains a programmatic identifier indicating the reason for the condition's last transition.
+                              Producers of specific condition types may define expected values and meanings for this field,
+                              and whether the values are considered a guaranteed API.
+                              The value should be a CamelCase string.
+                              This field may not be empty.
+                            maxLength: 1024
+                            minLength: 1
+                            pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                            type: string
+                          status:
+                            description: status of the condition, one of True, False,
+                              Unknown.
+                            enum:
+                            - "True"
+                            - "False"
+                            - Unknown
+                            type: string
+                          type:
+                            description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                            maxLength: 316
+                            pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                            type: string
+                        required:
+                        - lastTransitionTime
+                        - message
+                        - reason
+                        - status
+                        - type
+                        type: object
+                      maxItems: 8
+                      type: array
+                      x-kubernetes-list-map-keys:
+                      - type
+                      x-kubernetes-list-type: map
+                    exportingClusters:
+                      description: |-
+                        ExportingClusters is a list of clusters that exported the InferencePool(s) that back the
+                        InferencePoolImport. Required when the controller is responsible for CRUD'ing the InferencePoolImport
+                        from the exported InferencePool(s).
+                      items:
+                        description: ExportingCluster defines a cluster that exported
+                          the InferencePool that backs this InferencePoolImport.
+                        properties:
+                          name:
+                            description: Name of the exporting cluster (must be unique
+                              within the list).
+                            maxLength: 253
+                            minLength: 1
+                            type: string
+                        required:
+                        - name
+                        type: object
+                      type: array
+                    name:
+                      description: |-
+                        Name is a domain/path string that indicates the name of the controller that manages the
+                        InferencePoolImport. Name corresponds to the GatewayClass controllerName field when the
+                        controller will manage parents of type "Gateway". Otherwise, the name is implementation-specific.
+
+                        Example: "example.net/import-controller".
+
+                        The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes
+                        names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).
+
+                        A controller MUST populate this field when writing status and ensure that entries to status
+                        populated with their controller name are removed when they are no longer necessary.
+                      maxLength: 253
+                      minLength: 1
+                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$
+                      type: string
+                    parents:
+                      description: |-
+                        Parents is a list of parent resources, typically Gateways, that are associated with the
+                        InferencePoolImport, and the status of the InferencePoolImport with respect to each parent.
+
+                        Ancestor would be a more accurate name, but Parent is consistent with InferencePool terminology.
+
+                        Required when the controller manages the InferencePoolImport as an HTTPRoute backendRef. The controller
+                        must add an entry for each parent it manages and remove the parent entry when the controller no longer
+                        considers the InferencePoolImport to be associated with that parent.
+                      items:
+                        description: ParentStatus defines the observed state of InferencePool
+                          from a Parent, i.e. Gateway.
+                        properties:
+                          conditions:
+                            description: |-
+                              Conditions is a list of status conditions that provide information about the observed
+                              state of the InferencePool. This field is required to be set by the controller that
+                              manages the InferencePool.
+
+                              Supported condition types are:
+
+                              * "Accepted"
+                              * "ResolvedRefs"
+                            items:
+                              description: Condition contains details for one aspect
+                                of the current state of this API Resource.
+                              properties:
+                                lastTransitionTime:
+                                  description: |-
+                                    lastTransitionTime is the last time the condition transitioned from one status to another.
+                                    This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
+                                  format: date-time
+                                  type: string
+                                message:
+                                  description: |-
+                                    message is a human readable message indicating details about the transition.
+                                    This may be an empty string.
+                                  maxLength: 32768
+                                  type: string
+                                observedGeneration:
+                                  description: |-
+                                    observedGeneration represents the .metadata.generation that the condition was set based upon.
+                                    For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+                                    with respect to the current state of the instance.
+                                  format: int64
+                                  minimum: 0
+                                  type: integer
+                                reason:
+                                  description: |-
+                                    reason contains a programmatic identifier indicating the reason for the condition's last transition.
+                                    Producers of specific condition types may define expected values and meanings for this field,
+                                    and whether the values are considered a guaranteed API.
+                                    The value should be a CamelCase string.
+                                    This field may not be empty.
+                                  maxLength: 1024
+                                  minLength: 1
+                                  pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                                  type: string
+                                status:
+                                  description: status of the condition, one of True,
+                                    False, Unknown.
+                                  enum:
+                                  - "True"
+                                  - "False"
+                                  - Unknown
+                                  type: string
+                                type:
+                                  description: type of condition in CamelCase or in
+                                    foo.example.com/CamelCase.
+                                  maxLength: 316
+                                  pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                  type: string
+                              required:
+                              - lastTransitionTime
+                              - message
+                              - reason
+                              - status
+                              - type
+                              type: object
+                            maxItems: 8
+                            type: array
+                            x-kubernetes-list-map-keys:
+                            - type
+                            x-kubernetes-list-type: map
+                          controllerName:
+                            description: |-
+                              ControllerName is a domain/path string that indicates the name of the controller that
+                              wrote this status. This corresponds with the GatewayClass controllerName field when the
+                              parentRef references a Gateway kind.
+
+                              Example: "example.net/gateway-controller".
+
+                              The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names:
+
+                               https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+
+                              Controllers MAY populate this field when writing status. When populating this field, controllers
+                              should ensure that entries to status populated with their ControllerName are cleaned up when they
+                              are no longer necessary.
+                            maxLength: 253
+                            minLength: 1
+                            pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$
+                            type: string
+                          parentRef:
+                            description: |-
+                              ParentRef is used to identify the parent resource that this status
+                              is associated with. It is used to match the InferencePool with the parent
+                              resource, such as a Gateway.
+                            properties:
+                              group:
+                                default: gateway.networking.k8s.io
+                                description: |-
+                                  Group is the group of the referent API object. When unspecified, the referent is assumed
+                                  to be in the "gateway.networking.k8s.io" API group.
+                                maxLength: 253
+                                minLength: 0
+                                pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                type: string
+                              kind:
+                                default: Gateway
+                                description: |-
+                                  Kind is the kind of the referent API object. When unspecified, the referent is assumed
+                                  to be a "Gateway" kind.
+                                maxLength: 63
+                                minLength: 1
+                                pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
+                                type: string
+                              name:
+                                description: Name is the name of the referent API
+                                  object.
+                                maxLength: 253
+                                minLength: 1
+                                type: string
+                              namespace:
+                                description: |-
+                                  Namespace is the namespace of the referenced object. When unspecified, the local
+                                  namespace is inferred.
+
+                                  Note that when a namespace different than the local namespace is specified,
+                                  a ReferenceGrant object is required in the referent namespace to allow that
+                                  namespace's owner to accept the reference. See the ReferenceGrant
+                                  documentation for details: https://gateway-api.sigs.k8s.io/api-types/referencegrant/
+                                maxLength: 63
+                                minLength: 1
+                                pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                type: string
+                            required:
+                            - name
+                            type: object
+                        required:
+                        - parentRef
+                        type: object
+                      type: array
+                      x-kubernetes-list-type: atomic
+                  required:
+                  - name
+                  type: object
+                maxItems: 8
+                type: array
+                x-kubernetes-list-map-keys:
+                - name
+                x-kubernetes-list-type: map
+            required:
+            - controllers
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: null
+  storedVersions: null
diff --git a/conformance/resources/base.yaml b/conformance/resources/base.yaml
index a421f50..2e1b378 100644
--- a/conformance/resources/base.yaml
+++ b/conformance/resources/base.yaml
@@ -7,16 +7,16 @@
 apiVersion: v1
 kind: Namespace
 metadata:
-  name: gateway-conformance-infra
+  name: inference-conformance-infra
   labels:
-    gateway-conformance: infra
+    inference-conformance: infra
 ---
 apiVersion: v1
 kind: Namespace
 metadata:
-  name: gateway-conformance-app-backend
+  name: inference-conformance-app-backend
   labels:
-    gateway-conformance: backend
+    inference-conformance: backend
 ---
 # A basic Gateway resource that allows HTTPRoutes from the same namespace.
 # Tests can use this as a parent reference for routes that target InferencePools.
@@ -24,7 +24,7 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: Gateway
 metadata:
   name: conformance-primary
-  namespace: gateway-conformance-infra
+  namespace: inference-conformance-infra
 spec:
   gatewayClassName: "{GATEWAY_CLASS_NAME}"
   listeners:
@@ -42,7 +42,7 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: Gateway
 metadata:
   name: conformance-secondary
-  namespace: gateway-conformance-infra
+  namespace: inference-conformance-infra
 spec:
   gatewayClassName: "{GATEWAY_CLASS_NAME}"
   listeners:
@@ -55,14 +55,14 @@ spec:
         from: All
 
 ### The following defines the essential resources for the gateway conformance test.
-### All resources are created in the 'gateway-conformance-app-backend' namespace.
+### All resources are created in the 'inference-conformance-app-backend' namespace.
 ---
 # Deploys a mock backend service to act as a model server.
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: primary-inference-model-server-deployment
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
   labels:
     app: primary-inference-model-server
 spec:
@@ -106,7 +106,7 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: secondary-inference-model-server-deployment
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
   labels:
     app: secondary-inference-model-server
 spec:
@@ -150,7 +150,7 @@ apiVersion: inference.networking.k8s.io/v1
 kind: InferencePool
 metadata:
   name: primary-inference-pool
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   selector:
     matchLabels:
@@ -167,7 +167,7 @@ apiVersion: v1
 kind: Service
 metadata:
   name: primary-endpoint-picker-svc
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   selector:
     app: primary-app-backend-epp
@@ -183,7 +183,7 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: primary-app-endpoint-picker
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
   labels:
     app: primary-app-backend-epp
 spec:
@@ -206,7 +206,7 @@ spec:
         - --pool-name
         - "primary-inference-pool"
         - --pool-namespace
-        - "gateway-conformance-app-backend"
+        - "inference-conformance-app-backend"
         - --v
         - "4"
         - --zap-encoder
@@ -247,7 +247,7 @@ apiVersion: inference.networking.k8s.io/v1
 kind: InferencePool
 metadata:
   name: secondary-inference-pool
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   selector:
     matchLabels:
@@ -265,7 +265,7 @@ apiVersion: v1
 kind: Service
 metadata:
   name: secondary-endpoint-picker-svc
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   selector:
     app: secondary-app-backend-epp
@@ -281,7 +281,7 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: secondary-app-endpoint-picker
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
   labels:
     app: secondary-app-backend-epp
 spec:
@@ -304,7 +304,7 @@ spec:
         - --pool-name
         - "secondary-inference-pool"
         - --pool-namespace
-        - "gateway-conformance-app-backend"
+        - "inference-conformance-app-backend"
         - --v
         - "4"
         - --zap-encoder
@@ -344,7 +344,7 @@ apiVersion: v1
 kind: ConfigMap
 metadata:
   name: plugins-config
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 data:
   conformance-plugins.yaml: |
     apiVersion: inference.networking.x-k8s.io/v1alpha1
@@ -361,7 +361,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
 metadata:
   name: inference-model-reader
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 rules:
 - apiGroups: ["inference.networking.x-k8s.io"]
   resources: ["inferenceobjectives", "inferencepools"]
@@ -377,11 +377,11 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
 metadata:
   name: epp-to-inference-model-reader
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 subjects:
 - kind: ServiceAccount
   name: default
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 roleRef:
   kind: Role
   name: inference-model-reader
diff --git a/conformance/resources/resourcename.go b/conformance/resources/resourcename.go
index 4c0c7b4..4ac6437 100644
--- a/conformance/resources/resourcename.go
+++ b/conformance/resources/resourcename.go
@@ -19,8 +19,8 @@ package resources
 import "k8s.io/apimachinery/pkg/types"
 
 const (
-	AppBackendNamespace  = "gateway-conformance-app-backend"
-	InfraNamespace       = "gateway-conformance-infra"
+	AppBackendNamespace  = "inference-conformance-app-backend"
+	InfraNamespace       = "inference-conformance-infra"
 	PrimaryGatewayName   = "conformance-primary"
 	SecondaryGatewayName = "conformance-secondary"
 
diff --git a/conformance/scripts/istio/Makefile b/conformance/scripts/istio/Makefile
index 309d8c4..cfce3cc 100644
--- a/conformance/scripts/istio/Makefile
+++ b/conformance/scripts/istio/Makefile
@@ -3,7 +3,7 @@
 GATEWAY_API_VERSION ?= v1.3.0
 INFERENCE_EXTENSION_VERSION ?= v0.4.0
 ISTIO_VERSION ?= 1.27-alpha.0551127f00634403cddd4634567e65a8ecc499a7
-ISTIO_HUB ?= 
+ISTIO_HUB ?=
 ISTIO_PROFILE ?= minimal
 
 # Conformance test variables
@@ -72,7 +72,7 @@ apiVersion: networking.istio.io/v1
 kind: DestinationRule
 metadata:
   name: primary-endpoint-picker-tls
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   host: primary-endpoint-picker-svc
   trafficPolicy:
@@ -84,7 +84,7 @@ apiVersion: networking.istio.io/v1
 kind: DestinationRule
 metadata:
   name: secondary-endpoint-picker-tls
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   host: secondary-endpoint-picker-svc
   trafficPolicy:
@@ -432,7 +432,7 @@ setup-crds: setup-gateway-api-crds setup-inference-extension-crds
 # Setup TLS for EPP
 setup-tls:
 	@echo "Setting up TLS for EPP..."
-	-kubectl create namespace gateway-conformance-app-backend || true
+	-kubectl create namespace inference-conformance-app-backend || true
 	$(file >/tmp/tls-destination-rules.yaml,$(TLS_DESTINATION_RULES))
 	kubectl apply -f /tmp/tls-destination-rules.yaml
 	@rm -f /tmp/tls-destination-rules.yaml
@@ -518,7 +518,7 @@ readme-update: $(REPORT_BASE_DIR)/README.md
 # Clean up resources
 clean:
 	@echo "Cleaning up..."
-	kubectl delete namespace gateway-conformance-app-backend --ignore-not-found=true
+	kubectl delete namespace inference-conformance-app-backend --ignore-not-found=true
 	@echo "Cleaning up downloaded istioctl binaries..."
 	@rm -f $(ISTIOCTL_DIR)/istioctl-*
 	@echo "Note: If using minikube, run 'minikube delete' to completely clean up"
diff --git a/conformance/utils/traffic/traffic.go b/conformance/utils/traffic/traffic.go
index f53cc32..8acaefa 100644
--- a/conformance/utils/traffic/traffic.go
+++ b/conformance/utils/traffic/traffic.go
@@ -169,24 +169,56 @@ func waitForConvergeToExpected(
 	tlog.Logf(t, "Request passed")
 }
 
-// CompareRequestWithWildcardStatus compares requests with wildcard status code support.
-// It treats a single-digit expected code (e.g., 4) as a class wildcard (4xx),
-// while standard 3-digit codes are matched exactly.
-func CompareRequestWithWildcardStatus(t *testing.T, req *roundtripper.Request, cReq *roundtripper.CapturedRequest, cRes *roundtripper.CapturedResponse, expected gwhttp.ExpectedResponse) error {
-	if expected.Response.StatusCode < 1 || expected.Response.StatusCode >= 100 {
-		return gwhttp.CompareRequest(t, req, cReq, cRes, expected)
+// CompareRequestWithWildcardStatus compares requests allowing single-digit
+// status "classes" (e.g. 5 => any 5xx) via ExpectedResponse.Response.StatusCodes.
+// If no class wildcards are present, it defers to CompareRoundTrip.
+func CompareRequestWithWildcardStatus(
+	t *testing.T,
+	req *roundtripper.Request,
+	cReq *roundtripper.CapturedRequest,
+	cRes *roundtripper.CapturedResponse,
+	expected gwhttp.ExpectedResponse,
+) error {
+	// Separate specific status codes (>=100) from wildcard classes (1..9).
+	var wildcardClasses []int
+	var specificCodes []int
+	seenClass := map[int]struct{}{}
+	seenCode := map[int]struct{}{}
+
+	for _, sc := range expected.Response.StatusCodes {
+		switch {
+		case sc >= 100:
+			if _, ok := seenCode[sc]; !ok {
+				specificCodes = append(specificCodes, sc)
+				seenCode[sc] = struct{}{}
+			}
+		case sc >= 1 && sc <= 9:
+			if _, ok := seenClass[sc]; !ok {
+				wildcardClasses = append(wildcardClasses, sc)
+				seenClass[sc] = struct{}{}
+			}
+		}
 	}
 
-	expectedClass := expected.Response.StatusCode
+	// No wildcard classes? Defer to standard comparator.
+	if len(wildcardClasses) == 0 {
+		return gwhttp.CompareRoundTrip(t, req, cReq, cRes, expected)
+	}
+
+	// If the concrete status matches any wildcard class, materialize it and compare.
 	actualClass := cRes.StatusCode / 100
-	if expectedClass != actualClass {
-		return fmt.Errorf("expected status code class %dxx, but got %d", expectedClass, cRes.StatusCode)
+	for _, wc := range wildcardClasses {
+		if actualClass == wc {
+			modified := expected
+			// Keep any specific codes that were provided, but ensure the actual status is allowed.
+			modified.Response.StatusCodes = append(append([]int(nil), specificCodes...), cRes.StatusCode)
+			return gwhttp.CompareRoundTrip(t, req, cReq, cRes, modified)
+		}
 	}
 
-	// StatusCode Class matches; update status code on a copy to allow the standard comparator to pass.
-	modifiedExpected := expected
-	modifiedExpected.Response.StatusCode = cRes.StatusCode
-	return gwhttp.CompareRequest(t, req, cReq, cRes, modifiedExpected)
+	// Wildcards were provided but none matched the actual class—fail clearly.
+	return fmt.Errorf("expected status code class to be one of %vxx, got %d",
+		wildcardClasses, cRes.StatusCode)
 }
 
 // TODO: https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/1031
diff --git a/crd-ref-docs.yaml b/crd-ref-docs.yaml
index d00e6d0..5fefbff 100644
--- a/crd-ref-docs.yaml
+++ b/crd-ref-docs.yaml
@@ -4,7 +4,7 @@
 
 processor:
   ignoreTypes:
-    - "(InferencePool|InferenceObjective)List$"
+    - "(InferencePool|InferenceObjective|InferencePoolImport)List$"
   # RE2 regular expressions describing type fields that should be excluded from the generated documentation.
   ignoreFields:
     - "TypeMeta$"
diff --git a/docs/proposals/004-endpoint-picker-protocol/README.md b/docs/proposals/004-endpoint-picker-protocol/README.md
index 03b96bb..919bb63 100644
--- a/docs/proposals/004-endpoint-picker-protocol/README.md
+++ b/docs/proposals/004-endpoint-picker-protocol/README.md
@@ -14,6 +14,15 @@ This doc defines the protocol between the EPP and the proxy (e.g, Envoy).
 The EPP MUST implement the Envoy
 [external processing service](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto) protocol.
 
+## Version History
+
+| Version  | Date       | Changes                                          |
+|----------|------------|--------------------------------------------------|
+| v1.0.0   | 2025-07-29 | Added status metadata field for picked endpoints |
+| v0.4.0   | 2025-06-03 | Added support for multiple fallback endpoints    |
+| v0.3.0   | 2025-03-14 | Added subsetting and fallback support            |
+| v0.2.0   | 2025-02-22 | Initial protocol definition                      |
+
 ## Endpoint Subset
 
 [REQUEST: Data Plane -> EPP]
diff --git a/go.mod b/go.mod
index 2062225..982d46b 100644
--- a/go.mod
+++ b/go.mod
@@ -5,33 +5,38 @@ go 1.24.0
 require (
 	github.com/cespare/xxhash/v2 v2.3.0
 	github.com/elastic/crd-ref-docs v0.2.0
-	github.com/envoyproxy/go-control-plane/envoy v1.32.4
+	github.com/envoyproxy/go-control-plane/envoy v1.35.0
 	github.com/go-logr/logr v1.4.3
 	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.25.3
+	github.com/onsi/ginkgo/v2 v2.26.0
 	github.com/onsi/gomega v1.38.2
 	github.com/prometheus/client_golang v1.23.2
 	github.com/prometheus/client_model v0.6.2
-	github.com/prometheus/common v0.66.1
+	github.com/prometheus/common v0.67.1
 	github.com/prometheus/prometheus v0.306.0
 	github.com/stretchr/testify v1.11.1
+	go.opentelemetry.io/otel v1.38.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.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/sync v0.17.0
-	google.golang.org/grpc v1.75.1
-	google.golang.org/protobuf v1.36.9
+	google.golang.org/grpc v1.76.0
+	google.golang.org/protobuf v1.36.10
 	k8s.io/api v0.34.1
 	k8s.io/apiextensions-apiserver v0.34.1
 	k8s.io/apimachinery v0.34.1
 	k8s.io/client-go v0.34.1
 	k8s.io/code-generator v0.34.1
 	k8s.io/component-base v0.34.1
-	k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
-	sigs.k8s.io/controller-runtime v0.21.0
+	k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
+	sigs.k8s.io/controller-runtime v0.22.3
+	// Update the CONTROLLER_TOOLS_VERSION in Makefile when bumping controller-tools.
 	sigs.k8s.io/controller-tools v0.19.0
-	sigs.k8s.io/gateway-api v1.3.0
+	sigs.k8s.io/gateway-api v1.4.0
 	sigs.k8s.io/structured-merge-diff/v6 v6.3.0
 	sigs.k8s.io/yaml v1.6.0
 )
@@ -45,11 +50,11 @@ require (
 	github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
-	github.com/cenkalti/backoff/v5 v5.0.2 // indirect
+	github.com/cenkalti/backoff/v5 v5.0.3 // indirect
 	github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/dennwc/varint v1.0.0 // indirect
-	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
 	github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
 	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -57,9 +62,9 @@ require (
 	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/zapr v1.3.0 // indirect
-	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/jsonpointer v0.21.2 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
-	github.com/go-openapi/swag v0.23.0 // indirect
+	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
 	github.com/gobuffalo/flect v1.0.3 // indirect
 	github.com/goccy/go-yaml v1.18.0 // indirect
@@ -70,14 +75,14 @@ require (
 	github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
 	github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
 	github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
 	github.com/huandu/xstrings v1.3.3 // indirect
 	github.com/imdario/mergo v0.3.16 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
-	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/moby/spdystream v0.5.0 // indirect
@@ -85,50 +90,45 @@ require (
 	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
-	github.com/pkg/errors v0.9.1 // indirect
 	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
-	github.com/prometheus/procfs v0.16.1 // indirect
+	github.com/prometheus/procfs v0.17.0 // indirect
 	github.com/spf13/cobra v1.9.1 // indirect
 	github.com/spf13/pflag v1.0.7 // indirect
 	github.com/stoewer/go-strcase v1.3.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
-	go.opentelemetry.io/otel v1.37.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
-	go.opentelemetry.io/otel/metric v1.37.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.37.0 // indirect
-	go.opentelemetry.io/otel/trace v1.37.0 // indirect
-	go.opentelemetry.io/proto/otlp v1.6.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/proto/otlp v1.7.1 // indirect
 	go.uber.org/atomic v1.11.0 // indirect
 	go.uber.org/automaxprocs v1.6.0 // indirect
-	go.yaml.in/yaml/v2 v2.4.2 // indirect
+	go.yaml.in/yaml/v2 v2.4.3 // indirect
 	go.yaml.in/yaml/v3 v3.0.4 // indirect
 	golang.org/x/crypto v0.42.0 // indirect
 	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
 	golang.org/x/mod v0.28.0 // indirect
 	golang.org/x/net v0.44.0 // indirect
-	golang.org/x/oauth2 v0.30.0 // indirect
+	golang.org/x/oauth2 v0.31.0 // indirect
 	golang.org/x/sys v0.36.0 // indirect
 	golang.org/x/term v0.35.0 // indirect
 	golang.org/x/text v0.29.0 // indirect
 	golang.org/x/time v0.12.0 // indirect
 	golang.org/x/tools v0.37.0 // indirect
-	golang.org/x/tools/go/expect v0.1.1-deprecated // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
-	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apiserver v0.34.1 // indirect
-	k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect
+	k8s.io/gengo/v2 v2.0.0-20250820003526-c297c0c1eb9d // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
+	k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
-	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
 	sigs.k8s.io/randfill v1.0.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 6586a87..1805d42 100644
--- a/go.sum
+++ b/go.sum
@@ -60,8 +60,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
 github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
-github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
-github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
@@ -75,10 +75,10 @@ github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
 github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
 github.com/elastic/crd-ref-docs v0.2.0 h1:U17MyGX71j4qfKTvYxbR4qZGoA1hc2thy7kseGYmP+o=
 github.com/elastic/crd-ref-docs v0.2.0/go.mod h1:0bklkJhTG7nC6AVsdDi0wt5bGoqvzdZSzMMQkilZ6XM=
-github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
-github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
-github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
+github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
+github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo=
+github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
 github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
 github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
 github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
@@ -91,6 +91,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
 github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
+github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
+github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
+github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
+github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo=
+github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
 github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -98,12 +104,12 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
 github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=
+github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
 github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
 github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
@@ -143,8 +149,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
 github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
 github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
@@ -155,6 +161,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
+github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
 github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -169,8 +177,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
+github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
+github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
+github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@@ -196,8 +208,8 @@ 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.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
-github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
+github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE=
+github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw=
 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/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -215,10 +227,10 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
 github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
 github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
 github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
-github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
-github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
-github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
-github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
+github.com/prometheus/common v0.67.1 h1:OTSON1P4DNxzTg4hmKCc37o4ZAZDv0cfXLkOt0oEowI=
+github.com/prometheus/common v0.67.1/go.mod h1:RpmT9v35q2Y+lsieQsdOh5sXZ6ajUGC8NjZAmr8vb0Q=
+github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
+github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
 github.com/prometheus/prometheus v0.306.0 h1:Q0Pvz/ZKS6vVWCa1VSgNyNJlEe8hxdRlKklFg7SRhNw=
 github.com/prometheus/prometheus v0.306.0/go.mod h1:7hMSGyZHt0dcmZ5r4kFPJ/vxPQU99N5/BGwSPDxeZrQ=
 github.com/prometheus/sigv4 v0.2.0 h1:qDFKnHYFswJxdzGeRP63c4HlH3Vbn1Yf/Ao2zabtVXk=
@@ -244,6 +256,14 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -252,22 +272,24 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
-go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
-go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
-go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
-go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
-go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
-go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
-go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
-go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
-go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
-go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
-go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
+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/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/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=
 go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
 go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
@@ -278,8 +300,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
 go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
-go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -299,8 +321,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
 golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
-golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
-golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
+golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
+golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
 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=
@@ -339,19 +361,19 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
 gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
 google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo=
 google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
-google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
-google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
-google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
-google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
-google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
-google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
+google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
+google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
+google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
+google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
-gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
+gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@@ -375,24 +397,24 @@ k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc=
 k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg=
 k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
 k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
-k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q=
-k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
+k8s.io/gengo/v2 v2.0.0-20250820003526-c297c0c1eb9d h1:qUrYOinhdAUL0xxhA4gPqogPBaS9nIq2l2kTb6pmeB0=
+k8s.io/gengo/v2 v2.0.0-20250820003526-c297c0c1eb9d/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
-k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
-k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
-k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE=
+k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
+k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
-sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
-sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
+sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y=
+sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
 sigs.k8s.io/controller-tools v0.19.0 h1:OU7jrPPiZusryu6YK0jYSjPqg8Vhf8cAzluP9XGI5uk=
 sigs.k8s.io/controller-tools v0.19.0/go.mod h1:y5HY/iNDFkmFla2CfQoVb2AQXMsBk4ad84iR1PLANB0=
-sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M=
-sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk=
-sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
-sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ=
+sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
 sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
 sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
 sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
diff --git a/mkdocs.yml b/mkdocs.yml
index b746f74..4c285c2 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -82,11 +82,15 @@ nav:
     - Benchmark: performance/benchmark/index.md
     - Regression Testing: performance/regression-testing/index.md
   - Reference:
-    - API Reference: reference/spec.md
-    - Alpha API Reference: reference/x-spec.md
+    - v1 API Reference: reference/spec.md
+    - v1alpha1 API Reference:
+      - reference/x-v1a1-spec.md
+    - v1alpha2 API Reference:
+      - reference/x-v1a2-spec.md
     - API Types:
       - InferencePool: api-types/inferencepool.md
       - InferenceObjective: api-types/inferenceobjective.md
+      - InferencePoolImport: api-types/inferencepoolimport.md
   - Enhancements:
     - Overview: enhancements/overview.md
   - Contributing:
diff --git a/pkg/common/telemetry.go b/pkg/common/telemetry.go
new file mode 100644
index 0000000..3723e0f
--- /dev/null
+++ b/pkg/common/telemetry.go
@@ -0,0 +1,140 @@
+/*
+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 common
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"strconv"
+
+	"github.com/go-logr/logr"
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
+	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
+	"go.opentelemetry.io/otel/propagation"
+	"go.opentelemetry.io/otel/sdk/resource"
+	sdktrace "go.opentelemetry.io/otel/sdk/trace"
+	semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
+
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
+	"sigs.k8s.io/gateway-api-inference-extension/version"
+)
+
+type errorHandler struct {
+	logger logr.Logger
+}
+
+func (h *errorHandler) Handle(err error) {
+	h.logger.V(logging.DEFAULT).Error(err, "trace error occurred")
+}
+
+func InitTracing(ctx context.Context, logger logr.Logger) error {
+	logger = logger.WithName("trace")
+	loggerWrap := &errorHandler{logger: logger}
+
+	_, ok := os.LookupEnv("OTEL_SERVICE_NAME")
+	if !ok {
+		os.Setenv("OTEL_SERVICE_NAME", "gateway-api-inference-extension")
+	}
+
+	_, ok = os.LookupEnv("OTEL_EXPORTER_OTLP_ENDPOINT")
+	if !ok {
+		os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317")
+	}
+
+	traceExporter, err := initTraceExporter(ctx, logger)
+	if err != nil {
+		loggerWrap.Handle(fmt.Errorf("%s: %v", "init trace exporter failed", err))
+		return err
+	}
+
+	// Go SDK doesn't have an automatic sampler, handle manually
+	samplerType, ok := os.LookupEnv("OTEL_TRACES_SAMPLER")
+	if !ok {
+		samplerType = "parentbased_traceidratio"
+	}
+	samplerARG, ok := os.LookupEnv("OTEL_TRACES_SAMPLER_ARG")
+	if !ok {
+		samplerARG = "0.1"
+	}
+
+	sampler := sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))
+	if samplerType == "parentbased_traceidratio" {
+		fraction, err := strconv.ParseFloat(samplerARG, 64)
+		if err != nil {
+			fraction = 0.1
+		}
+
+		sampler = sdktrace.ParentBased(sdktrace.TraceIDRatioBased(fraction))
+	} else {
+		loggerWrap.Handle(fmt.Errorf("unsupported sampler type: %s, fallback to parentbased_traceidratio with 0.1 Ratio", samplerType))
+	}
+
+	opt := []sdktrace.TracerProviderOption{
+		sdktrace.WithBatcher(traceExporter),
+		sdktrace.WithSampler(sampler),
+		sdktrace.WithResource(resource.NewWithAttributes(
+			semconv.SchemaURL,
+			semconv.ServiceVersionKey.String(version.BuildRef),
+		)),
+	}
+
+	tracerProvider := sdktrace.NewTracerProvider(opt...)
+	otel.SetTracerProvider(tracerProvider)
+	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
+	otel.SetErrorHandler(loggerWrap)
+
+	go func() {
+		<-ctx.Done()
+		err := tracerProvider.Shutdown(context.Background())
+		if err != nil {
+			loggerWrap.Handle(fmt.Errorf("%s: %v", "failed to shutdown TraceProvider", err))
+		}
+
+		logger.V(logging.DEFAULT).Info("trace provider shutting down")
+	}()
+
+	return nil
+}
+
+// initTraceExporter create a SpanExporter
+// support exporter type
+// - console: export spans in console for development use case
+// - otlp: export spans through gRPC to an opentelemetry collector
+func initTraceExporter(ctx context.Context, logger logr.Logger) (sdktrace.SpanExporter, error) {
+	var traceExporter sdktrace.SpanExporter
+	traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
+	if err != nil {
+		return nil, fmt.Errorf("failed to create stdouttrace exporter: %w", err)
+	}
+
+	exporterType, ok := os.LookupEnv("OTEL_TRACES_EXPORTER")
+	if !ok {
+		exporterType = "console"
+	}
+
+	logger.Info("init OTel trace exporter", "type", exporterType)
+	if exporterType == "otlp" {
+		traceExporter, err = otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure())
+		if err != nil {
+			return nil, fmt.Errorf("failed to create otlp-grcp exporter: %w", err)
+		}
+	}
+
+	return traceExporter, nil
+}
diff --git a/pkg/epp/handlers/request.go b/pkg/epp/handlers/request.go
index 7f81221..0e04289 100644
--- a/pkg/epp/handlers/request.go
+++ b/pkg/epp/handlers/request.go
@@ -29,6 +29,13 @@ import (
 	errutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/error"
 )
 
+const (
+	// defaultFairnessID is the default fairness ID used when no ID is provided in the request.
+	// This ensures that requests without explicit fairness identifiers are still grouped and managed by the Flow Control
+	// system.
+	defaultFairnessID = "default-flow"
+)
+
 func (s *StreamingServer) HandleRequestHeaders(reqCtx *RequestContext, req *extProcPb.ProcessingRequest_RequestHeaders) error {
 	reqCtx.RequestReceivedTimestamp = time.Now()
 
@@ -80,6 +87,11 @@ func (s *StreamingServer) HandleRequestHeaders(reqCtx *RequestContext, req *extP
 			delete(reqCtx.Request.Headers, header.Key)
 		}
 	}
+
+	if reqCtx.FairnessID == "" {
+		reqCtx.FairnessID = defaultFairnessID
+	}
+
 	return nil
 }
 
diff --git a/pkg/epp/plugins/handle.go b/pkg/epp/plugins/handle.go
index 8c9153c..c074e90 100644
--- a/pkg/epp/plugins/handle.go
+++ b/pkg/epp/plugins/handle.go
@@ -19,6 +19,8 @@ package plugins
 import (
 	"context"
 	"fmt"
+
+	backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
 )
 
 // Handle provides plugins a set of standard data and tools to work with
@@ -27,6 +29,9 @@ type Handle interface {
 	Context() context.Context
 
 	HandlePlugins
+
+	// PodList lists pods matching the given predicate.
+	PodList(predicate func(backendmetrics.PodMetrics) bool) []backendmetrics.PodMetrics
 }
 
 // HandlePlugins defines a set of APIs to work with instantiated plugins
@@ -44,10 +49,14 @@ type HandlePlugins interface {
 	GetAllPluginsWithNames() map[string]Plugin
 }
 
+// PodListFunc is a function type that filters and returns a list of pod metrics
+type PodListFunc func(predicate func(backendmetrics.PodMetrics) bool) []backendmetrics.PodMetrics
+
 // eppHandle is an implementation of the interface plugins.Handle
 type eppHandle struct {
 	ctx context.Context
 	HandlePlugins
+	podList PodListFunc
 }
 
 // Context returns a context the plugins can use, if they need one
@@ -84,12 +93,18 @@ func (h *eppHandlePlugins) GetAllPluginsWithNames() map[string]Plugin {
 	return h.plugins
 }
 
-func NewEppHandle(ctx context.Context) Handle {
+// PodList lists pods matching the given predicate.
+func (h *eppHandle) PodList(predicate func(backendmetrics.PodMetrics) bool) []backendmetrics.PodMetrics {
+	return h.podList(predicate)
+}
+
+func NewEppHandle(ctx context.Context, podList PodListFunc) Handle {
 	return &eppHandle{
 		ctx: ctx,
 		HandlePlugins: &eppHandlePlugins{
 			plugins: map[string]Plugin{},
 		},
+		podList: podList,
 	}
 }
 
diff --git a/pkg/epp/requestcontrol/admission.go b/pkg/epp/requestcontrol/admission.go
new file mode 100644
index 0000000..383d284
--- /dev/null
+++ b/pkg/epp/requestcontrol/admission.go
@@ -0,0 +1,219 @@
+/*
+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 requestcontrol
+
+import (
+	"context"
+	"time"
+
+	"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/types"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/handlers"
+	errutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/error"
+	logutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
+	requtil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/request"
+)
+
+// AdmissionController defines the interface for making admission control decisions.
+// Implementations of this interface determine whether an incoming inference request should be accepted or rejected
+// based on various criteria such as system load, fairness, priority, and available capacity.
+type AdmissionController interface {
+	// Admit determines if a request should be admitted.
+	// It is called by the Director for each incoming request.
+	//
+	// 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:
+	//   - nil: If the request is admitted and should proceed to scheduling.
+	//   - errutil.Error: If the request is rejected.
+	Admit(
+		ctx context.Context,
+		reqCtx *handlers.RequestContext,
+		candidatePods []backendmetrics.PodMetrics,
+		priority int,
+	) error
+}
+
+// saturationDetector defines the minimal interface required for checking if the backend pool is saturated.
+type saturationDetector interface {
+	IsSaturated(ctx context.Context, candidatePods []backendmetrics.PodMetrics) bool
+}
+
+// flowController defines the minimal interface required by FlowControlAdmissionController for enqueuing requests and
+// waiting for an admission outcome.
+type flowController interface {
+	EnqueueAndWait(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.
+func rejectIfSheddableAndSaturated(
+	ctx context.Context,
+	sd saturationDetector,
+	reqCtx *handlers.RequestContext,
+	candidatePods []backendmetrics.PodMetrics,
+	priority int,
+) error {
+	if requtil.IsSheddable(priority) {
+		logger := log.FromContext(ctx)
+		if sd.IsSaturated(ctx, candidatePods) {
+			logger.V(logutil.TRACE).Info("Request rejected: system saturated and request is sheddable",
+				"requestID", reqCtx.SchedulingRequest.RequestId)
+			return errutil.Error{
+				Code: errutil.InferencePoolResourceExhausted,
+				Msg:  "system saturated, sheddable request dropped",
+			}
+		}
+	}
+	return nil
+}
+
+// --- LegacyAdmissionController ---
+
+// LegacyAdmissionController implements saturation-based admission control.
+// It rejects sheddable requests (priority < 0) if the saturationDetector indicates that the system is currently
+// saturated. Non-sheddable requests always bypass the saturation check.
+type LegacyAdmissionController struct {
+	saturationDetector saturationDetector
+}
+
+// NewLegacyAdmissionController creates a new LegacyAdmissionController.
+func NewLegacyAdmissionController(sd saturationDetector) *LegacyAdmissionController {
+	return &LegacyAdmissionController{saturationDetector: sd}
+}
+
+// Admit implements the AdmissionController interface for the legacy strategy.
+// It checks for saturation only for requests with priority < 0.
+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 {
+		return err
+	}
+	logger.V(logutil.TRACE).Info("Request admitted", "requestID", reqCtx.SchedulingRequest.RequestId)
+	return nil
+}
+
+// --- 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.
+type FlowControlAdmissionController struct {
+	saturationDetector saturationDetector
+	flowController     flowController
+}
+
+// NewFlowControlAdmissionController creates a new FlowControlAdmissionController.
+// It requires a SaturationDetector and a flowController instance.
+func NewFlowControlAdmissionController(sd saturationDetector, fc flowController) *FlowControlAdmissionController {
+	return &FlowControlAdmissionController{
+		saturationDetector: sd,
+		flowController:     fc,
+	}
+}
+
+// Admit implements the AdmissionController interface by checking for saturation on sheddable requests first, then
+// deferring to the Flow Control system.
+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{
+		ctx:             ctx,
+		requestID:       reqCtx.SchedulingRequest.RequestId,
+		fairnessID:      reqCtx.FairnessID,
+		priority:        priority,
+		requestByteSize: uint64(reqCtx.RequestSize),
+		candidatePods:   candidatePods,
+	}
+
+	outcome, err := fcac.flowController.EnqueueAndWait(fcReq)
+	logger.V(logutil.DEBUG).Info("Flow control outcome",
+		"requestID", reqCtx.SchedulingRequest.RequestId, "outcome", outcome, "error", err)
+	return translateFlowControlOutcome(outcome, err)
+}
+
+// flowControlRequest is an adapter that implements the types.FlowControlRequest interface.
+type flowControlRequest struct {
+	ctx             context.Context
+	requestID       string
+	fairnessID      string
+	priority        int
+	requestByteSize uint64
+	candidatePods   []backendmetrics.PodMetrics
+}
+
+var _ types.FlowControlRequest = &flowControlRequest{}
+
+func (r *flowControlRequest) Context() context.Context           { return r.ctx }
+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}
+}
+
+// translateFlowControlOutcome maps the context-rich outcome of the Flow Control layer to the public errutil.Error
+// contract used by the Director.
+func translateFlowControlOutcome(outcome types.QueueOutcome, err error) error {
+	msg := "request rejected by flow control"
+	if err != nil {
+		msg = err.Error()
+	}
+
+	switch outcome {
+	case types.QueueOutcomeDispatched:
+		return nil
+	case types.QueueOutcomeRejectedCapacity:
+		return errutil.Error{Code: errutil.InferencePoolResourceExhausted, Msg: msg}
+	case types.QueueOutcomeEvictedTTL:
+		return errutil.Error{Code: errutil.ServiceUnavailable, Msg: "request timed out in queue: " + msg}
+	case types.QueueOutcomeEvictedContextCancelled:
+		return errutil.Error{Code: errutil.ServiceUnavailable, Msg: "client disconnected: " + msg}
+	case types.QueueOutcomeRejectedOther, types.QueueOutcomeEvictedOther:
+		return errutil.Error{Code: errutil.Internal, Msg: "internal flow control error: " + msg}
+	default:
+		return errutil.Error{Code: errutil.Internal, Msg: "unhandled flow control outcome: " + msg}
+	}
+}
diff --git a/pkg/epp/requestcontrol/director.go b/pkg/epp/requestcontrol/director.go
index a3e2d6d..2f8c738 100644
--- a/pkg/epp/requestcontrol/director.go
+++ b/pkg/epp/requestcontrol/director.go
@@ -54,28 +54,36 @@ type Scheduler interface {
 	Schedule(ctx context.Context, request *schedulingtypes.LLMRequest, candidatePods []schedulingtypes.Pod) (result *schedulingtypes.SchedulingResult, err error)
 }
 
-// SaturationDetector provides a signal indicating whether the backends are considered saturated.
-type SaturationDetector interface {
-	IsSaturated(ctx context.Context, candidatePods []backendmetrics.PodMetrics) bool
-}
-
 // NewDirectorWithConfig creates a new Director instance with all dependencies.
-func NewDirectorWithConfig(datastore Datastore, scheduler Scheduler, saturationDetector SaturationDetector, config *Config) *Director {
+func NewDirectorWithConfig(
+	datastore Datastore,
+	scheduler Scheduler,
+	admissionController AdmissionController,
+	config *Config,
+) *Director {
 	return &Director{
 		datastore:           datastore,
 		scheduler:           scheduler,
-		saturationDetector:  saturationDetector,
+		admissionController: admissionController,
 		preRequestPlugins:   config.preRequestPlugins,
 		postResponsePlugins: config.postResponsePlugins,
 		defaultPriority:     0, // define default priority explicitly
 	}
 }
 
-// Director orchestrates the request handling flow, including scheduling.
+// Director orchestrates the request handling flow after initial parsing by the handler.
+// Its responsibilities include:
+// - Retrieving request metadata and relevant objectives.
+// - Determining candidate pods.
+// - Performing admission control via the AdmissionController.
+// - Scheduling the request to target pod(s) via the Scheduler.
+// - Running PreRequest plugins.
+// - Preparing the request context for the Envoy ext_proc filter to route the request.
+// - Running PostResponse plugins.
 type Director struct {
 	datastore           Datastore
 	scheduler           Scheduler
-	saturationDetector  SaturationDetector
+	admissionController AdmissionController
 	preRequestPlugins   []PreRequest
 	postResponsePlugins []PostResponse
 	// we just need a pointer to an int variable since priority is a pointer in InferenceObjective
@@ -140,8 +148,8 @@ func (d *Director) HandleRequest(ctx context.Context, reqCtx *handlers.RequestCo
 		return reqCtx, errutil.Error{Code: errutil.ServiceUnavailable, Msg: "failed to find candidate pods for serving the request"}
 	}
 
-	// Admission Control check
-	if err := d.admitRequest(ctx, candidatePods, *infObjective.Spec.Priority, reqCtx.FairnessID); err != nil {
+	if err := d.admissionController.Admit(ctx, reqCtx, candidatePods, *infObjective.Spec.Priority); err != nil {
+		logger.V(logutil.DEFAULT).Info("Request rejected by admission control", "error", err)
 		return reqCtx, err
 	}
 
@@ -207,31 +215,6 @@ func (d *Director) getCandidatePodsForScheduling(ctx context.Context, requestMet
 	return podFilteredList
 }
 
-// admitRequest handles admission control to decide whether or not to accept the request
-// based on the request priority and saturation state.
-func (d *Director) admitRequest(ctx context.Context, candidatePods []backendmetrics.PodMetrics, requestPriority int, fairnessID string) error {
-	loggerTrace := log.FromContext(ctx).V(logutil.TRACE)
-
-	loggerTrace.Info("Entering Flow Control", "priority", requestPriority, "fairnessID", fairnessID)
-
-	// This will be removed in favor of a more robust implementation (Flow Control) in the very near future.
-	// TODO: Make this a configurable value.
-	// Tracking issue https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/1347
-	if requestPriority >= 0 {
-		loggerTrace.Info("Non-sheddable request bypassing saturation check.")
-		return nil
-	}
-
-	if d.saturationDetector.IsSaturated(ctx, candidatePods) {
-		return errutil.Error{
-			Code: errutil.InferencePoolResourceExhausted,
-			Msg:  "system saturated, sheddable request dropped",
-		}
-	}
-
-	return nil
-}
-
 // prepareRequest populates the RequestContext and calls the registered PreRequest plugins
 // for allowing plugging customized logic based on the scheduling result.
 func (d *Director) prepareRequest(ctx context.Context, reqCtx *handlers.RequestContext, result *schedulingtypes.SchedulingResult) (*handlers.RequestContext, error) {
diff --git a/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer.go b/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer.go
index bd9e2c9..8b68132 100644
--- a/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer.go
+++ b/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer.go
@@ -149,3 +149,35 @@ func (i *indexer) reportLRUSize(ctx context.Context, interval time.Duration) {
 		i.mu.RUnlock()
 	}
 }
+
+// RemovePod removes a pod and its associated entries from the indexer.
+func (i *indexer) RemovePod(pod ServerID) {
+	i.mu.RLock()
+	lruCache, exists := i.podToLRU[pod]
+	i.mu.RUnlock()
+
+	if !exists {
+		return
+	}
+
+	// Remove all hashes associated with the pod from hashToPods (triggers eviction callbacks).
+	for _, hash := range lruCache.Keys() {
+		lruCache.Remove(hash)
+	}
+
+	i.mu.Lock()
+	delete(i.podToLRU, pod)
+	i.mu.Unlock()
+}
+
+// Pods returns the list of all pods currently tracked in the indexer.
+func (i *indexer) Pods() []ServerID {
+	i.mu.RLock()
+	defer i.mu.RUnlock()
+
+	pods := make([]ServerID, 0, len(i.podToLRU))
+	for pod := range i.podToLRU {
+		pods = append(pods, pod)
+	}
+	return pods
+}
diff --git a/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go b/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
index 2870288..16af272 100644
--- a/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
+++ b/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
@@ -22,11 +22,13 @@ import (
 	"encoding/json"
 	"fmt"
 	"sync"
+	"time"
 
 	"github.com/cespare/xxhash/v2"
 	k8stypes "k8s.io/apimachinery/pkg/types"
 	"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/metrics"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/requestcontrol"
@@ -57,6 +59,10 @@ const (
 	PrefixCachePluginType = "prefix-cache-scorer"
 )
 
+const (
+	PodActiveCheckInterval = 2 * time.Minute
+)
+
 var DefaultConfig = Config{
 	BlockSize:              DefaultBlockSize,
 	MaxPrefixBlocksToMatch: DefaultMaxPrefixBlocks,
@@ -88,6 +94,8 @@ type podSet map[ServerID]struct{}
 type Indexer interface {
 	Get(hash BlockHash) podSet
 	Add(hashes []BlockHash, server ServerID)
+	RemovePod(server ServerID)
+	Pods() []ServerID
 }
 
 // BlockHash is a hash of the block of request body.
@@ -144,7 +152,9 @@ func PrefixCachePluginFactory(name string, rawParameters json.RawMessage, handle
 		}
 	}
 
-	return New(handle.Context(), parameters).WithName(name), nil
+	p := New(handle.Context(), parameters).WithName(name)
+	go p.CleanUpInactivePods(handle.Context(), handle)
+	return p, nil
 }
 
 // New initializes a new prefix Plugin and returns its pointer.
@@ -256,6 +266,33 @@ func (p *Plugin) matchLongestPrefix(ctx context.Context, hashes []BlockHash) map
 	return res
 }
 
+// CleanUpInactivePods starts a goroutine that watches for inactive pods.
+func (m *Plugin) CleanUpInactivePods(ctx context.Context, handle plugins.Handle) {
+	logger := log.FromContext(ctx).V(logutil.VERBOSE)
+	ticker := time.NewTicker(PodActiveCheckInterval)
+	defer ticker.Stop()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			activePodMetrics := handle.PodList(func(_ backendmetrics.PodMetrics) bool { return true })
+			activePods := make(map[ServerID]struct{}, len(activePodMetrics))
+			for _, pm := range activePodMetrics {
+				activePods[ServerID(pm.GetPod().NamespacedName)] = struct{}{}
+			}
+
+			for _, pod := range m.indexer.Pods() {
+				if _, ok := activePods[pod]; !ok {
+					m.indexer.RemovePod(pod)
+					logger.Info("Removed pod not in active set", "pod", pod)
+				}
+			}
+		}
+	}
+}
+
 // hashPrompt divides the prompt into blocks and calculate the prefix cache for each block.
 // hash[0] is calculated including the model name and cache_salt(if provided), since different models generally don't share prefix cache.
 // For block i, hash(i) = hash(block i content, hash(i-1)).
diff --git a/pkg/epp/server/runserver.go b/pkg/epp/server/runserver.go
index 870acc7..1d9d1d1 100644
--- a/pkg/epp/server/runserver.go
+++ b/pkg/epp/server/runserver.go
@@ -42,6 +42,7 @@ import (
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datastore"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/handlers"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/requestcontrol"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/saturationdetector"
 )
 
 // ExtProcServerRunner provides methods to manage an external process server.
@@ -56,7 +57,7 @@ type ExtProcServerRunner struct {
 	RefreshPrometheusMetricsInterval time.Duration
 	MetricsStalenessThreshold        time.Duration
 	Director                         *requestcontrol.Director
-	SaturationDetector               requestcontrol.SaturationDetector
+	SaturationDetector               *saturationdetector.Detector
 	UseExperimentalDatalayerV2       bool // Pluggable data layer feature flag
 
 	// This should only be used in tests. We won't need this once we do not inject metrics in the tests.
diff --git a/pkg/epp/util/request/sheddable.go b/pkg/epp/util/request/sheddable.go
new file mode 100644
index 0000000..c2f32c1
--- /dev/null
+++ b/pkg/epp/util/request/sheddable.go
@@ -0,0 +1,22 @@
+/*
+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 request
+
+// IsSheddable determines if a request is considered sheddable based on its priority.
+func IsSheddable(priority int) bool {
+	return priority < 0
+}
diff --git a/pkg/generator/main.go b/pkg/generator/main.go
index 3cb70a5..c663588 100644
--- a/pkg/generator/main.go
+++ b/pkg/generator/main.go
@@ -35,6 +35,7 @@ import (
 func main() {
 	roots, err := loader.LoadRoots(
 		"k8s.io/apimachinery/pkg/runtime/schema", // Needed to parse generated register functions.
+		"sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha1",
 		"sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2",
 		"sigs.k8s.io/gateway-api-inference-extension/api/v1",
 	)
diff --git a/site-src/api-types/inferenceobjective.md b/site-src/api-types/inferenceobjective.md
index 4ce6a7c..8c48651 100644
--- a/site-src/api-types/inferenceobjective.md
+++ b/site-src/api-types/inferenceobjective.md
@@ -15,4 +15,4 @@ To associate a request to the InferencePool with a specific InferenceObjective,
 
 ## Spec
 
-The full spec of the InferenceObjective is defined [here](/reference/x-spec/#inferenceobjective).
\ No newline at end of file
+The full spec of the InferenceObjective is defined [here](/reference/x-v1-a2-spec/#inferenceobjective).
diff --git a/site-src/api-types/inferencepoolimport.md b/site-src/api-types/inferencepoolimport.md
new file mode 100644
index 0000000..aa58247
--- /dev/null
+++ b/site-src/api-types/inferencepoolimport.md
@@ -0,0 +1,26 @@
+# Inference Pool Import
+
+??? example "Alpha since v1.1.0"
+
+    The `InferencePoolImport` resource is alpha and may have breaking changes in
+    future releases of the API.
+
+## Background
+
+The **InferencePoolImport** API is a cluster-local, controller-managed resource that represents an imported InferencePool.
+It primarily communicates a relationship between an exported InferencePool and the exporting cluster name. It is not
+user-authored; status carries the effective import. Inference Platform Owners can reference the InferencePoolImport,
+even if the local cluster does not have an InferencePool. In the context of Gateway API, it means that an HTTPRoute can
+be configured to reference an InferencePoolImport to route matching requests to endpoints of backing InferencePools.
+
+Key ideas:
+
+- Map an exported InferencePool to exporting controller and cluster.
+- Name/namespace sameness with the exported InferencePool (avoids extra indirection).
+- Conditions: Surface a controller-level status condition to indicate whether the InferencePoolImport is ready for use.
+- Conditions: Surface parent-level status conditions to indicate whether the InferencePoolImport is referenced by a parent,
+  e.g. Gateway.
+
+## Spec
+
+The full spec of the InferencePoolImport is defined [here](/reference/x-v1a1-spec/#inferencepoolimport).
diff --git a/site-src/contributing/index.md b/site-src/contributing/index.md
index 7f56b82..eb5da17 100644
--- a/site-src/contributing/index.md
+++ b/site-src/contributing/index.md
@@ -46,4 +46,4 @@ doc. Feel free to add topics for discussion at an upcoming meeting.
 
 All meetings are recorded and automatically uploaded to the [WG-Serving meetings
 YouTube
-playlist](https://www.youtube.com/playlist?list=PL69nYSiGNLP30qNanabU75ayPK7OPNAAS).
+playlist](https://www.youtube.com/playlist?list=PL69nYSiGNLP2io2Gg92njBfh-DX9sk7O3).
diff --git a/site-src/reference/spec.md b/site-src/reference/spec.md
index 11080fe..666ebe3 100644
--- a/site-src/reference/spec.md
+++ b/site-src/reference/spec.md
@@ -15,6 +15,32 @@ inference.networking.k8s.io API group.
 
 
 
+#### ControllerName
+
+_Underlying type:_ _string_
+
+ControllerName is the name of a controller that manages ParentStatus. It must be a domain prefixed
+path.
+
+Valid values include:
+
+* "example.com/bar"
+
+Invalid values include:
+
+* "example.com" - must include path
+* "foo.example.com" - must include path
+
+_Validation:_
+- MaxLength: 253
+- MinLength: 1
+- Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
+
+_Appears in:_
+- [ParentStatus](#parentstatus)
+
+
+
 #### EndpointPickerFailureMode
 
 _Underlying type:_ _string_
@@ -124,7 +150,7 @@ _Appears in:_
 | Field | Description | Default | Validation |
 | --- | --- | --- | --- |
 | `selector` _[LabelSelector](#labelselector)_ | Selector determines which Pods are members of this inference pool.<br />It matches Pods by their labels only within the same namespace; cross-namespace<br />selection is not supported.<br />The structure of this LabelSelector is intentionally simple to be compatible<br />with Kubernetes Service selectors, as some implementations may translate<br />this configuration into a Service resource. |  |  |
-| `targetPorts` _[Port](#port) array_ | TargetPorts defines a list of ports that are exposed by this InferencePool.<br />Currently, the list may only include a single port definition. |  | MaxItems: 1 <br />MinItems: 1 <br /> |
+| `targetPorts` _[Port](#port) array_ | TargetPorts defines a list of ports that are exposed by this InferencePool.<br />Every port will be treated as a distinctive endpoint by EPP,<br />addressable as a 'podIP:portNumber' combination. |  | MaxItems: 8 <br />MinItems: 1 <br /> |
 | `endpointPickerRef` _[EndpointPickerRef](#endpointpickerref)_ | EndpointPickerRef is a reference to the Endpoint Picker extension and its<br />associated configuration. |  |  |
 
 
@@ -329,6 +355,7 @@ _Appears in:_
 | --- | --- | --- | --- |
 | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | Conditions is a list of status conditions that provide information about the observed<br />state of the InferencePool. This field is required to be set by the controller that<br />manages the InferencePool.<br />Supported condition types are:<br />* "Accepted"<br />* "ResolvedRefs" |  | MaxItems: 8 <br /> |
 | `parentRef` _[ParentReference](#parentreference)_ | ParentRef is used to identify the parent resource that this status<br />is associated with. It is used to match the InferencePool with the parent<br />resource, such as a Gateway. |  |  |
+| `controllerName` _[ControllerName](#controllername)_ | ControllerName is a domain/path string that indicates the name of the controller that<br />wrote this status. This corresponds with the GatewayClass controllerName field when the<br />parentRef references a Gateway kind.<br />Example: "example.net/gateway-controller".<br />The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names:<br /> https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names<br />Controllers MAY populate this field when writing status. When populating this field, controllers<br />should ensure that entries to status populated with their ControllerName are cleaned up when they<br />are no longer necessary. |  | MaxLength: 253 <br />MinLength: 1 <br />Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$` <br /> |
 
 
 #### Port
diff --git a/site-src/reference/x-v1a1-spec.md b/site-src/reference/x-v1a1-spec.md
new file mode 100644
index 0000000..55bec76
--- /dev/null
+++ b/site-src/reference/x-v1a1-spec.md
@@ -0,0 +1,126 @@
+# API Reference
+
+## Packages
+- [inference.networking.x-k8s.io/v1alpha1](#inferencenetworkingx-k8siov1alpha1)
+
+
+## inference.networking.x-k8s.io/v1alpha1
+
+Package v1alpha1 contains API Schema definitions for the
+inference.networking.x-k8s.io API group.
+
+
+### Resource Types
+- [InferencePoolImport](#inferencepoolimport)
+
+
+
+#### ClusterName
+
+_Underlying type:_ _string_
+
+ClusterName is the name of a cluster that exported the InferencePool.
+
+_Validation:_
+- MaxLength: 253
+- MinLength: 1
+
+_Appears in:_
+- [ExportingCluster](#exportingcluster)
+
+
+
+#### ControllerName
+
+_Underlying type:_ _string_
+
+ControllerName is the name of a controller that manages a resource. It must be a domain prefixed path.
+
+Valid values include:
+
+  - "example.com/bar"
+
+Invalid values include:
+
+  - "example.com" - must include path
+  - "foo.example.com" - must include path
+
+_Validation:_
+- MaxLength: 253
+- MinLength: 1
+- Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
+
+_Appears in:_
+- [ImportController](#importcontroller)
+
+
+
+#### ExportingCluster
+
+
+
+ExportingCluster defines a cluster that exported the InferencePool that backs this InferencePoolImport.
+
+
+
+_Appears in:_
+- [ImportController](#importcontroller)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `name` _[ClusterName](#clustername)_ | Name of the exporting cluster (must be unique within the list). |  | MaxLength: 253 <br />MinLength: 1 <br />Required: \{\} <br /> |
+
+
+#### ImportController
+
+
+
+ImportController defines a controller that is responsible for managing the InferencePoolImport.
+
+
+
+_Appears in:_
+- [InferencePoolImportStatus](#inferencepoolimportstatus)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `name` _[ControllerName](#controllername)_ | Name is a domain/path string that indicates the name of the controller that manages the<br />InferencePoolImport. Name corresponds to the GatewayClass controllerName field when the<br />controller will manage parents of type "Gateway". Otherwise, the name is implementation-specific.<br />Example: "example.net/import-controller".<br />The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes<br />names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).<br />A controller MUST populate this field when writing status and ensure that entries to status<br />populated with their controller name are removed when they are no longer necessary. |  | MaxLength: 253 <br />MinLength: 1 <br />Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$` <br /> |
+| `exportingClusters` _[ExportingCluster](#exportingcluster) array_ | ExportingClusters is a list of clusters that exported the InferencePool(s) that back the<br />InferencePoolImport. Required when the controller is responsible for CRUD'ing the InferencePoolImport<br />from the exported InferencePool(s). |  |  |
+| `parents` _ParentStatus array_ | Parents is a list of parent resources, typically Gateways, that are associated with the<br />InferencePoolImport, and the status of the InferencePoolImport with respect to each parent.<br />Ancestor would be a more accurate name, but Parent is consistent with InferencePool terminology.<br />Required when the controller manages the InferencePoolImport as an HTTPRoute backendRef. The controller<br />must add an entry for each parent it manages and remove the parent entry when the controller no longer<br />considers the InferencePoolImport to be associated with that parent. |  |  |
+| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | Conditions track the state of the InferencePoolImport.<br />Known condition types are:<br /> * "Accepted" |  | MaxItems: 8 <br /> |
+
+
+#### InferencePoolImport
+
+
+
+InferencePoolImport is the Schema for the InferencePoolImports API.
+
+
+
+
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `apiVersion` _string_ | `inference.networking.x-k8s.io/v1alpha1` | | |
+| `kind` _string_ | `InferencePoolImport` | | |
+| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |  |  |
+| `status` _[InferencePoolImportStatus](#inferencepoolimportstatus)_ | Status defines the observed state of the InferencePoolImport. |  |  |
+
+
+#### InferencePoolImportStatus
+
+
+
+InferencePoolImportStatus defines the observed state of the InferencePoolImport.
+
+
+
+_Appears in:_
+- [InferencePoolImport](#inferencepoolimport)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `controllers` _[ImportController](#importcontroller) array_ | Controllers is a list of controllers that are responsible for managing the InferencePoolImport. |  | MaxItems: 8 <br />Required: \{\} <br /> |
+
+
diff --git a/site-src/reference/x-spec.md b/site-src/reference/x-v1a2-spec.md
similarity index 100%
rename from site-src/reference/x-spec.md
rename to site-src/reference/x-v1a2-spec.md


Test patch:
diff --git a/conformance/tests/epp_unavailable_fail_open.yaml b/conformance/tests/epp_unavailable_fail_open.yaml
index 9388a01..df3a6f0 100644
--- a/conformance/tests/epp_unavailable_fail_open.yaml
+++ b/conformance/tests/epp_unavailable_fail_open.yaml
@@ -2,13 +2,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-failopen-pool-gw
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-secondary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "secondary.example.com"
diff --git a/conformance/tests/gateway_following_epp_routing.go b/conformance/tests/gateway_following_epp_routing.go
index c320ad3..eaa6181 100644
--- a/conformance/tests/gateway_following_epp_routing.go
+++ b/conformance/tests/gateway_following_epp_routing.go
@@ -174,7 +174,7 @@ func assertTrafficOnlyReachesToExpectedPods(t *testing.T, suite *suite.Conforman
 			if err != nil {
 				return fmt.Errorf("failed to roundtrip request: %w", err)
 			}
-			if err := gwhttp.CompareRequest(t, &req, cReq, cRes, expected); err != nil {
+			if err := gwhttp.CompareRoundTrip(t, &req, cReq, cRes, expected); err != nil {
 				return fmt.Errorf("response expectation failed for request: %w", err)
 			}
 
diff --git a/conformance/tests/gateway_following_epp_routing.yaml b/conformance/tests/gateway_following_epp_routing.yaml
index c4db638..d0f441a 100644
--- a/conformance/tests/gateway_following_epp_routing.yaml
+++ b/conformance/tests/gateway_following_epp_routing.yaml
@@ -2,13 +2,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-primary-gw
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "primary.example.com"
@@ -21,4 +21,3 @@ spec:
     - path:
         type: PathPrefix
         value: /primary-gateway-test
-  
\ No newline at end of file
diff --git a/conformance/tests/gateway_weighted_two_pools.go b/conformance/tests/gateway_weighted_two_pools.go
new file mode 100644
index 0000000..bbe3cbb
--- /dev/null
+++ b/conformance/tests/gateway_weighted_two_pools.go
@@ -0,0 +1,221 @@
+/*
+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 tests
+
+import (
+	"fmt"
+	"math"
+	"net/http"
+	"strings"
+	"sync/atomic"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"golang.org/x/sync/errgroup"
+	"k8s.io/apimachinery/pkg/types"
+	gwhttp "sigs.k8s.io/gateway-api/conformance/utils/http"
+	"sigs.k8s.io/gateway-api/conformance/utils/suite"
+	"sigs.k8s.io/gateway-api/pkg/features"
+
+	"sigs.k8s.io/gateway-api-inference-extension/conformance/resources"
+	k8sutils "sigs.k8s.io/gateway-api-inference-extension/conformance/utils/kubernetes"
+	"sigs.k8s.io/gateway-api-inference-extension/conformance/utils/traffic"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/test"
+)
+
+func init() {
+	ConformanceTests = append(ConformanceTests, GatewayWeightedAcrossTwoInferencePools)
+}
+
+// GatewayWeightedAcrossTwoInferencePools verifies that Gateway splits traffic across two
+// InferencePools according to backendRef weights, and that each request is routed to an
+// endpoint of the selected InferencePool.
+var GatewayWeightedAcrossTwoInferencePools = suite.ConformanceTest{
+	ShortName:   "GatewayWeightedAcrossTwoInferencePools",
+	Description: "Gateway should split traffic across two InferencePools based on backendRef weights and route only to endpoints of the selected InferencePool",
+	Manifests:   []string{"tests/gateway_weighted_two_pools.yaml"},
+	Features: []features.FeatureName{
+		features.SupportGateway,
+		features.FeatureName("SupportInferencePool"),
+	},
+	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
+		const (
+			hostname = "primary.example.com"
+			path     = "/weighted-two-pools-test"
+
+			// Sample size so the weight signal dominates random noise.
+			totalRequests      = 200
+			concurrentRequests = 5
+
+			// These route weights must match the test manifest.
+			primaryWeight   = 70
+			secondaryWeight = 30
+		)
+
+		// Objects under test.
+		httpRouteNN := types.NamespacedName{Name: "httproute-weighted-two-pools", Namespace: resources.AppBackendNamespace}
+		gatewayNN := resources.PrimaryGatewayNN
+		primaryPoolNN := resources.PrimaryInferencePoolNN
+		secondaryPoolNN := types.NamespacedName{Name: "secondary-inference-pool", Namespace: resources.AppBackendNamespace}
+
+		// Labels for the two deployments defined in base.yaml.
+		primaryLabels := map[string]string{"app": "primary-inference-model-server"}
+		secondaryLabels := map[string]string{"app": "secondary-inference-model-server"}
+
+		t.Log("Verifying HTTPRoute and both InferencePools are accepted and the Gateway has an address.")
+		k8sutils.HTTPRouteMustBeAcceptedAndResolved(t, s.Client, s.TimeoutConfig, httpRouteNN, gatewayNN)
+		k8sutils.InferencePoolMustBeAcceptedByParent(t, s.Client, primaryPoolNN, gatewayNN)
+		k8sutils.InferencePoolMustBeAcceptedByParent(t, s.Client, secondaryPoolNN, gatewayNN)
+		gwAddr := k8sutils.GetGatewayEndpoint(t, s.Client, s.TimeoutConfig, gatewayNN)
+
+		// Discover pods for each pool and build quick lookup sets.
+		t.Logf("Fetching primary backend pods with labels: %v", primaryLabels)
+		primaryPods, err := k8sutils.GetPodsWithLabel(t, s.Client, resources.AppBackendNamespace, primaryLabels, s.TimeoutConfig)
+		require.NoError(t, err)
+		require.Len(t, primaryPods, 3) // base.yaml uses 3 replicas
+
+		t.Logf("Fetching secondary backend pods with labels: %v", secondaryLabels)
+		secondaryPods, err := k8sutils.GetPodsWithLabel(t, s.Client, resources.AppBackendNamespace, secondaryLabels, s.TimeoutConfig)
+		require.NoError(t, err)
+		require.Len(t, secondaryPods, 3) // base.yaml uses 3 replicas
+
+		primaryPodNames := make([]string, 0, len(primaryPods))
+		primaryPodIPs := make([]string, 0, len(primaryPods))
+		for _, p := range primaryPods {
+			require.NotEmpty(t, p.Status.PodIP, "primary pod %s has no IP yet", p.Name)
+			primaryPodNames = append(primaryPodNames, p.Name)
+			primaryPodIPs = append(primaryPodIPs, p.Status.PodIP)
+		}
+
+		secondaryPodNames := make([]string, 0, len(secondaryPods))
+		secondaryPodIPs := make([]string, 0, len(secondaryPods))
+		for _, p := range secondaryPods {
+			require.NotEmpty(t, p.Status.PodIP, "secondary pod %s has no IP yet", p.Name)
+			secondaryPodNames = append(secondaryPodNames, p.Name)
+			secondaryPodIPs = append(secondaryPodIPs, p.Status.PodIP)
+		}
+
+		// Send one targeted request per backend Pod to ensure EPP readiness.
+		allIPs := append(append([]string{}, primaryPodIPs...), secondaryPodIPs...)
+		allNames := append(append([]string{}, primaryPodNames...), secondaryPodNames...)
+		for i := 0; i < len(allIPs); i++ {
+			traffic.MakeRequestAndExpectSuccess(
+				t,
+				s.RoundTripper,
+				s.TimeoutConfig,
+				gwAddr,
+				traffic.Request{
+					Host: hostname,
+					Path: path,
+					Headers: map[string]string{
+						test.HeaderTestEppEndPointSelectionKey: allIPs[i],
+					},
+					Method:    http.MethodPost,
+					Body:      `{"model":"conformance-fake-model","prompt":"Warmup"}`,
+					Backend:   allNames[i],
+					Namespace: resources.AppBackendNamespace,
+				},
+			)
+		}
+
+		// Provide a union list of eligible endpoints for the test. Each pool's EPP
+		// should filter to endpoints that actually belong to its pool.
+		eppHeaderValue := strings.Join(allIPs, ",")
+
+		requestBody := `{
+			"model": "conformance-fake-model",
+			"prompt": "Write as if you were a critic: San Francisco"
+		}`
+
+		// Build quick lookup sets for attributing each hit to a pool by backend pod name.
+		primarySet := make(map[string]struct{}, len(primaryPodNames))
+		for _, n := range primaryPodNames {
+			primarySet[n] = struct{}{}
+		}
+		secondarySet := make(map[string]struct{}, len(secondaryPodNames))
+		for _, n := range secondaryPodNames {
+			secondarySet[n] = struct{}{}
+		}
+
+		headers := map[string]string{
+			test.HeaderTestEppEndPointSelectionKey: eppHeaderValue,
+		}
+		expected := gwhttp.ExpectedResponse{
+			Request: gwhttp.Request{
+				Host:    hostname,
+				Path:    path,
+				Method:  http.MethodPost,
+				Headers: headers,
+			},
+			Response: gwhttp.Response{
+				StatusCode: http.StatusOK,
+			},
+			Namespace: resources.AppBackendNamespace,
+		}
+		req := gwhttp.MakeRequest(t, &expected, gwAddr, "HTTP", "http")
+
+		var primaryHits, secondaryHits atomic.Int64
+		var g errgroup.Group
+		g.SetLimit(concurrentRequests)
+
+		for i := 0; i < totalRequests; i++ {
+			g.Go(func() error {
+				cReq, cRes, err := traffic.MakeCallRoundTripper(t, s.RoundTripper, &traffic.RequestWithBody{
+					Request: req,
+					Body:    strings.NewReader(requestBody),
+				})
+				if err != nil {
+					return fmt.Errorf("failed to roundtrip request: %w", err)
+				}
+				if err := gwhttp.CompareRoundTrip(t, &req, cReq, cRes, expected); err != nil {
+					return fmt.Errorf("response expectation failed: %w", err)
+				}
+
+				// Attribute response to pool by backend pod name.
+				if _, ok := primarySet[cReq.Pod]; ok {
+					primaryHits.Add(1)
+				} else if _, ok := secondarySet[cReq.Pod]; ok {
+					secondaryHits.Add(1)
+				} else {
+					return fmt.Errorf("request was handled by unexpected pod %q (not in either pool)", cReq.Pod)
+				}
+				return nil
+			})
+		}
+		require.NoError(t, g.Wait(), "requests failed")
+
+		ph := float64(primaryHits.Load())
+		sh := float64(secondaryHits.Load())
+		total := ph + sh
+		require.Equal(t, int64(totalRequests), int64(total), "sum of hits must equal number of attempts")
+		require.Greater(t, total, 0.0)
+
+		observedPrimary := ph / total
+		expectedPrimary := float64(primaryWeight) / float64(primaryWeight+secondaryWeight)
+
+		// Allow either a 10 percentage-point absolute error, or a 3-sigma binomial CI.
+		sigma := math.Sqrt(expectedPrimary * (1.0 - expectedPrimary) / total)
+		absTolerance := math.Max(0.10, 3.0*sigma)
+
+		diff := math.Abs(observedPrimary - expectedPrimary)
+		require.LessOrEqualf(t, diff, absTolerance,
+			"weighted split out of bounds: observed primary=%.3f (hits=%d/%d), expected=%.3f, tolerance=±%.3f",
+			observedPrimary, int64(ph), int64(total), expectedPrimary, absTolerance)
+		t.Logf("Weighted split OK: primary=%.3f (hits=%d/%d), expected=%.3f, tolerance=±%.3f; secondary hits=%d",
+			observedPrimary, int64(ph), int64(total), expectedPrimary, absTolerance, int64(sh))
+	},
+}
diff --git a/conformance/tests/gateway_weighted_two_pools.yaml b/conformance/tests/gateway_weighted_two_pools.yaml
new file mode 100644
index 0000000..2882822
--- /dev/null
+++ b/conformance/tests/gateway_weighted_two_pools.yaml
@@ -0,0 +1,30 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: httproute-weighted-two-pools
+  namespace: gateway-conformance-app-backend
+spec:
+  parentRefs:
+  - group: gateway.networking.k8s.io
+    kind: Gateway
+    name: conformance-primary
+    namespace: gateway-conformance-infra
+    sectionName: http
+  hostnames:
+  - "primary.example.com"
+  rules:
+  - matches:
+    - path:
+        type: PathPrefix
+        value: /weighted-two-pools-test
+    backendRefs:
+    # 70% of traffic goes to the primary pool
+    - group: inference.networking.k8s.io
+      kind: InferencePool
+      name: primary-inference-pool
+      weight: 70
+    # 30% of traffic goes to the secondary pool
+    - group: inference.networking.k8s.io
+      kind: InferencePool
+      name: secondary-inference-pool
+      weight: 30
diff --git a/conformance/tests/httproute_invalid_inferencepool_ref.yaml b/conformance/tests/httproute_invalid_inferencepool_ref.yaml
index 15e7ad5..a0954cd 100644
--- a/conformance/tests/httproute_invalid_inferencepool_ref.yaml
+++ b/conformance/tests/httproute_invalid_inferencepool_ref.yaml
@@ -2,13 +2,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-to-non-existent-pool
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   rules:
   - backendRefs:
diff --git a/conformance/tests/httproute_multiple_gateways_different_pools.yaml b/conformance/tests/httproute_multiple_gateways_different_pools.yaml
index caded16..17bf494 100644
--- a/conformance/tests/httproute_multiple_gateways_different_pools.yaml
+++ b/conformance/tests/httproute_multiple_gateways_different_pools.yaml
@@ -3,12 +3,12 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: route-for-primary-gateway
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
   hostnames:
   - "primary.example.com"
   rules:
@@ -25,12 +25,12 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: route-for-secondary-gateway
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - kind: Gateway
     name: conformance-secondary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
   hostnames:
   - "secondary.example.com"
   rules:
diff --git a/conformance/tests/inferencepool_accepted.yaml b/conformance/tests/inferencepool_accepted.yaml
index 59710ba..b155ae2 100644
--- a/conformance/tests/inferencepool_accepted.yaml
+++ b/conformance/tests/inferencepool_accepted.yaml
@@ -3,20 +3,20 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-inferencepool-accepted
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   rules:
   - backendRefs:
     - group: inference.networking.k8s.io
       kind: InferencePool
       name: primary-inference-pool
-      # namespace: gateway-conformance-app-backend - is omitted since it is in the same namespace as HTTPRoute
+      # namespace: inference-conformance-app-backend - is omitted since it is in the same namespace as HTTPRoute
     matches:
     - path:
         type: PathPrefix
diff --git a/conformance/tests/inferencepool_httproute_port_validation.yaml b/conformance/tests/inferencepool_httproute_port_validation.yaml
index 9c78117..53d8455 100644
--- a/conformance/tests/inferencepool_httproute_port_validation.yaml
+++ b/conformance/tests/inferencepool_httproute_port_validation.yaml
@@ -4,13 +4,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-pool-port-unspecified
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "port-unspecified.example.com"
@@ -30,13 +30,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-pool-port-matching
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "port-matching.example.com"
@@ -56,13 +56,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-pool-port-non-matching
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "port-non-matching.example.com"
diff --git a/conformance/tests/inferencepool_invalid_epp_service.yaml b/conformance/tests/inferencepool_invalid_epp_service.yaml
index b3dc70e..3f3b8c4 100644
--- a/conformance/tests/inferencepool_invalid_epp_service.yaml
+++ b/conformance/tests/inferencepool_invalid_epp_service.yaml
@@ -2,7 +2,7 @@ apiVersion: inference.networking.k8s.io/v1
 kind: InferencePool
 metadata:
   name: pool-with-invalid-epp
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   selector:
     matchLabels:
@@ -19,11 +19,11 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-invalid-epp-pool
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
   rules:
   - backendRefs:
     - name: pool-with-invalid-epp
diff --git a/conformance/tests/inferencepool_multiple_rules_different_pools.yaml b/conformance/tests/inferencepool_multiple_rules_different_pools.yaml
index 2dd8f4a..0539a11 100644
--- a/conformance/tests/inferencepool_multiple_rules_different_pools.yaml
+++ b/conformance/tests/inferencepool_multiple_rules_different_pools.yaml
@@ -3,11 +3,11 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-multiple-rules-different-pools
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
     - name: conformance-primary
-      namespace: gateway-conformance-infra
+      namespace: inference-conformance-infra
   rules:
     - matches:
         - path:
diff --git a/conformance/tests/inferencepool_resolvedrefs_condition.yaml b/conformance/tests/inferencepool_resolvedrefs_condition.yaml
index 8947168..05a2318 100644
--- a/conformance/tests/inferencepool_resolvedrefs_condition.yaml
+++ b/conformance/tests/inferencepool_resolvedrefs_condition.yaml
@@ -8,13 +8,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-primary-gw
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-primary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "primary.example.com"
@@ -33,13 +33,13 @@ apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
 metadata:
   name: httproute-for-secondary-gw
-  namespace: gateway-conformance-app-backend
+  namespace: inference-conformance-app-backend
 spec:
   parentRefs:
   - group: gateway.networking.k8s.io
     kind: Gateway
     name: conformance-secondary
-    namespace: gateway-conformance-infra
+    namespace: inference-conformance-infra
     sectionName: http
   hostnames:
   - "secondary.example.com"
diff --git a/pkg/epp/controller/inferencepool_reconciler_test.go b/pkg/epp/controller/inferencepool_reconciler_test.go
index 7f69385..553fcc7 100644
--- a/pkg/epp/controller/inferencepool_reconciler_test.go
+++ b/pkg/epp/controller/inferencepool_reconciler_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
@@ -180,7 +181,9 @@ type diffStoreParams struct {
 
 func diffStore(datastore datastore.Datastore, params diffStoreParams) string {
 	gotPool, _ := datastore.PoolGet()
-	if diff := cmp.Diff(params.wantPool, gotPool); diff != "" {
+	// controller-runtime fake client may not populate TypeMeta (APIVersion/Kind).
+	// Ignore it when comparing pools.
+	if diff := cmp.Diff(params.wantPool, gotPool, cmpopts.IgnoreTypes(metav1.TypeMeta{})); diff != "" {
 		return "pool:" + diff
 	}
 
@@ -333,7 +336,10 @@ func xDiffStore(t *testing.T, datastore datastore.Datastore, params xDiffStorePa
 	if err != nil {
 		t.Fatalf("failed to convert InferencePool to XInferencePool: %v", err)
 	}
-	if diff := cmp.Diff(params.wantPool, gotXPool); diff != "" {
+
+	// controller-runtime fake client may not populate TypeMeta (APIVersion/Kind).
+	// Ignore it when comparing pools.
+	if diff := cmp.Diff(params.wantPool, gotXPool, cmpopts.IgnoreTypes(metav1.TypeMeta{})); diff != "" {
 		return "pool:" + diff
 	}
 
diff --git a/pkg/epp/handlers/request_test.go b/pkg/epp/handlers/request_test.go
index 4ae2078..a3ef90c 100644
--- a/pkg/epp/handlers/request_test.go
+++ b/pkg/epp/handlers/request_test.go
@@ -21,6 +21,7 @@ import (
 
 	configPb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	extProcPb "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
+	"github.com/stretchr/testify/assert"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/metadata"
 )
 
@@ -66,3 +67,32 @@ func TestHandleRequestHeaders(t *testing.T) {
 		t.Errorf("expected fairness ID header to be removed from request headers, but it was not")
 	}
 }
+
+func TestHandleRequestHeaders_DefaultFairnessID(t *testing.T) {
+	t.Parallel()
+
+	server := &StreamingServer{}
+	reqCtx := &RequestContext{
+		Request: &Request{
+			Headers: make(map[string]string),
+		},
+	}
+
+	req := &extProcPb.ProcessingRequest_RequestHeaders{
+		RequestHeaders: &extProcPb.HttpHeaders{
+			Headers: &configPb.HeaderMap{
+				Headers: []*configPb.HeaderValue{
+					{
+						Key:   "x-test-header",
+						Value: "test-value",
+					},
+				},
+			},
+			EndOfStream: false,
+		},
+	}
+
+	err := server.HandleRequestHeaders(reqCtx, req)
+	assert.NoError(t, err, "expected no error")
+	assert.Equal(t, defaultFairnessID, reqCtx.FairnessID, "expected fairness ID to be defaulted")
+}
diff --git a/pkg/epp/requestcontrol/admission_test.go b/pkg/epp/requestcontrol/admission_test.go
new file mode 100644
index 0000000..002c50f
--- /dev/null
+++ b/pkg/epp/requestcontrol/admission_test.go
@@ -0,0 +1,282 @@
+/*
+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 requestcontrol
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
+	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"
+	errutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/error"
+	logutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
+)
+
+// --- 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
+	called  bool
+}
+
+func (m *mockFlowController) EnqueueAndWait(_ fctypes.FlowControlRequest) (fctypes.QueueOutcome, error) {
+	m.called = true
+	return m.outcome, m.err
+}
+
+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"},
+	}
+
+	testCases := []struct {
+		name            string
+		priority        int
+		isSaturated     bool
+		expectErr       bool
+		expectErrCode   string
+		expectErrSubstr string
+	}{
+		{
+			name:        "non_sheddable_saturated_admit",
+			priority:    0,
+			isSaturated: true,
+			expectErr:   false,
+		},
+		{
+			name:        "sheddable_not_saturated_admit",
+			priority:    -1,
+			isSaturated: false,
+			expectErr:   false,
+		},
+		{
+			name:            "sheddable_saturated_reject",
+			priority:        -1,
+			isSaturated:     true,
+			expectErr:       true,
+			expectErrCode:   errutil.InferencePoolResourceExhausted,
+			expectErrSubstr: "system saturated, sheddable request dropped",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			saturationDetector := &mockSaturationDetector{isSaturated: tc.isSaturated}
+			ac := NewLegacyAdmissionController(saturationDetector)
+
+			err := ac.Admit(ctx, reqCtx, candidatePods, tc.priority)
+
+			if !tc.expectErr {
+				assert.NoError(t, err, "Admit() should not have returned an error for scenario: %s", tc.name)
+			} else {
+				require.Error(t, err, "Admit() should have returned an error for scenario: %s", tc.name)
+				var e errutil.Error
+				if assert.ErrorAs(t, err, &e, "error should be of type errutil.Error") {
+					assert.Equal(t, tc.expectErrCode, e.Code, "incorrect error code for scenario: %s", tc.name)
+					assert.Contains(t, e.Msg, tc.expectErrSubstr, "incorrect error message substring for scenario: %s", tc.name)
+				}
+			}
+		})
+	}
+}
+
+func TestFlowControlRequestAdapter(t *testing.T) {
+	t.Parallel()
+	ctx := context.Background()
+	candidatePods := []backendmetrics.PodMetrics{&backendmetrics.FakePodMetrics{}}
+
+	testCases := []struct {
+		name            string
+		requestID       string
+		fairnessID      string
+		priority        int
+		requestByteSize uint64
+		expectFlowKey   fctypes.FlowKey
+	}{
+		{
+			name:            "simple",
+			requestID:       "req-1",
+			fairnessID:      "flow-1",
+			priority:        10,
+			requestByteSize: 1024,
+			expectFlowKey:   fctypes.FlowKey{ID: "flow-1", Priority: 10},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			fcReq := &flowControlRequest{
+				ctx:             ctx,
+				requestID:       tc.requestID,
+				fairnessID:      tc.fairnessID,
+				priority:        tc.priority,
+				requestByteSize: tc.requestByteSize,
+				candidatePods:   candidatePods,
+			}
+
+			assert.Equal(t, ctx, fcReq.Context(), "Context() mismatch")
+			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"},
+	}
+
+	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:        "non_sheddable_saturated_dispatch",
+			priority:    0,
+			isSaturated: true,
+			fcOutcome:   fctypes.QueueOutcomeDispatched,
+			expectErr:   false,
+		},
+		{
+			name:            "fc_reject_capacity",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeRejectedCapacity,
+			expectErr:       true,
+			expectErrCode:   errutil.InferencePoolResourceExhausted,
+			expectErrSubstr: "request rejected by flow control",
+		},
+		{
+			name:            "fc_evict_ttl",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeEvictedTTL,
+			fcErr:           errors.New("timeout"),
+			expectErr:       true,
+			expectErrCode:   errutil.ServiceUnavailable,
+			expectErrSubstr: "request timed out in queue: timeout",
+		},
+		{
+			name:            "fc_evict_context_cancelled",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeEvictedContextCancelled,
+			expectErr:       true,
+			expectErrCode:   errutil.ServiceUnavailable,
+			expectErrSubstr: "client disconnected",
+		},
+		{
+			name:            "fc_reject_other",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeRejectedOther,
+			expectErr:       true,
+			expectErrCode:   errutil.Internal,
+			expectErrSubstr: "internal flow control error",
+		},
+		{
+			name:            "fc_evict_other",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeEvictedOther,
+			fcErr:           errors.New("internal error"),
+			expectErr:       true,
+			expectErrCode:   errutil.Internal,
+			expectErrSubstr: "internal flow control error: internal error",
+		},
+		{
+			name:            "fc_unhandled_outcome",
+			priority:        0,
+			fcOutcome:       fctypes.QueueOutcomeNotYetFinalized,
+			expectErr:       true,
+			expectErrCode:   errutil.Internal,
+			expectErrSubstr: "unhandled flow control outcome",
+		},
+	}
+
+	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)
+
+			err := ac.Admit(ctx, reqCtx, candidatePods, 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)
+			}
+
+			if !tc.expectErr {
+				assert.NoError(t, err, "Admit() returned an unexpected error for scenario: %s", tc.name)
+			} else {
+				require.Error(t, err, "Admit() should have returned an error for scenario: %s", tc.name)
+				var e errutil.Error
+				if assert.ErrorAs(t, err, &e, "error should be of type errutil.Error") {
+					assert.Equal(t, tc.expectErrCode, e.Code, "incorrect error code for scenario: %s", tc.name)
+					assert.Contains(t, e.Msg, tc.expectErrSubstr, "incorrect error message substring for scenario: %s", tc.name)
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/epp/requestcontrol/director_test.go b/pkg/epp/requestcontrol/director_test.go
index a0cb7c3..777e287 100644
--- a/pkg/epp/requestcontrol/director_test.go
+++ b/pkg/epp/requestcontrol/director_test.go
@@ -50,12 +50,17 @@ import (
 
 // --- Mocks ---
 
-type mockSaturationDetector struct {
-	isSaturated bool
+type mockAdmissionController struct {
+	admitErr error
 }
 
-func (m *mockSaturationDetector) IsSaturated(_ context.Context, _ []backendmetrics.PodMetrics) bool {
-	return m.isSaturated
+func (m *mockAdmissionController) Admit(
+	_ context.Context,
+	_ *handlers.RequestContext,
+	_ []backendmetrics.PodMetrics,
+	_ int,
+) error {
+	return m.admitErr
 }
 
 type mockScheduler struct {
@@ -71,8 +76,12 @@ type mockDatastore struct {
 	pods []backendmetrics.PodMetrics
 }
 
-func (ds *mockDatastore) PoolGet() (*v1.InferencePool, error)                { return nil, nil }
-func (ds *mockDatastore) ObjectiveGet(_ string) *v1alpha2.InferenceObjective { return nil }
+func (ds *mockDatastore) PoolGet() (*v1.InferencePool, error) {
+	return nil, nil
+}
+func (ds *mockDatastore) ObjectiveGet(_ string) *v1alpha2.InferenceObjective {
+	return nil
+}
 func (ds *mockDatastore) PodList(predicate func(backendmetrics.PodMetrics) bool) []backendmetrics.PodMetrics {
 	res := []backendmetrics.PodMetrics{}
 	for _, pod := range ds.pods {
@@ -187,23 +196,23 @@ func TestDirector_HandleRequest(t *testing.T) {
 	}
 
 	tests := []struct {
-		name                   string
-		reqBodyMap             map[string]any
-		mockSaturationDetector *mockSaturationDetector
-		inferenceObjectiveName string
-		schedulerMockSetup     func(m *mockScheduler)
-		wantErrCode            string                   // Expected errutil code string
-		wantReqCtx             *handlers.RequestContext // Fields to check in the returned RequestContext
-		wantMutatedBodyModel   string                   // Expected model in reqCtx.Request.Body after PostDispatch
-		targetModelName        string                   // Expected model name after target model resolution
+		name                    string
+		reqBodyMap              map[string]any
+		mockAdmissionController *mockAdmissionController
+		inferenceObjectiveName  string
+		schedulerMockSetup      func(m *mockScheduler)
+		wantErrCode             string                   // Expected errutil code string
+		wantReqCtx              *handlers.RequestContext // Fields to check in the returned RequestContext
+		wantMutatedBodyModel    string                   // Expected model in reqCtx.Request.Body after PostDispatch
+		targetModelName         string                   // Expected model name after target model resolution
 	}{
 		{
-			name: "successful completions request (critical, saturation ignored)",
+			name: "successful completions request",
 			reqBodyMap: map[string]any{
 				"model":  model,
 				"prompt": "critical prompt",
 			},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: true},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleResults = defaultSuccessfulScheduleResults
 			},
@@ -221,7 +230,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 			targetModelName:        model,
 		},
 		{
-			name: "successful chat completions request (default critical, saturation ignored)",
+			name: "successful chat completions request",
 			reqBodyMap: map[string]any{
 				"model": model,
 				"messages": []any{
@@ -231,7 +240,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 					},
 				},
 			},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: true},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleResults = defaultSuccessfulScheduleResults
 			},
@@ -247,7 +256,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 			targetModelName:      model,
 		},
 		{
-			name: "successful chat completions request with multiple messages (critical, saturation ignored)",
+			name: "successful chat completions request with multiple messages",
 			reqBodyMap: map[string]any{
 				"model": model,
 				"messages": []any{
@@ -261,6 +270,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 					},
 				},
 			},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleResults = defaultSuccessfulScheduleResults
 			},
@@ -277,36 +287,13 @@ func TestDirector_HandleRequest(t *testing.T) {
 			inferenceObjectiveName: objectiveName,
 			targetModelName:        model,
 		},
-		{
-			name: "successful completions request (sheddable, not saturated)",
-			reqBodyMap: map[string]any{
-				"model":  modelSheddable,
-				"prompt": "sheddable prompt",
-			},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: false},
-			schedulerMockSetup: func(m *mockScheduler) {
-				m.scheduleResults = defaultSuccessfulScheduleResults
-			},
-			wantReqCtx: &handlers.RequestContext{
-				ObjectiveKey:    objectiveNameSheddable,
-				TargetModelName: modelSheddable,
-				TargetPod: &backend.Pod{
-					NamespacedName: types.NamespacedName{Namespace: "default", Name: "pod1"},
-					Address:        "192.168.1.100",
-				},
-				TargetEndpoint: "192.168.1.100:8000,192.168.2.100:8000,192.168.4.100:8000",
-			},
-			wantMutatedBodyModel:   modelSheddable,
-			inferenceObjectiveName: objectiveNameSheddable,
-			targetModelName:        modelSheddable,
-		},
 		{
 			name: "successful request with target model resolution",
 			reqBodyMap: map[string]any{
 				"model":  modelWithResolvedTarget,
 				"prompt": "prompt for target resolution",
 			},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: false},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleResults = defaultSuccessfulScheduleResults
 			},
@@ -342,28 +329,27 @@ func TestDirector_HandleRequest(t *testing.T) {
 				"model":  "food-review-1",
 				"prompt": "test prompt",
 			},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: false},
-			inferenceObjectiveName: "food-review-1",
-			targetModelName:        "food-review-1",
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
+			inferenceObjectiveName:  "food-review-1",
+			targetModelName:         "food-review-1",
 		},
 		{
 
-			name: "request dropped (sheddable, saturated)",
+			name: "request rejected by admission controller",
 			reqBodyMap: map[string]any{
 				"model":  modelSheddable,
 				"prompt": "sheddable prompt",
 			},
-			inferenceObjectiveName: objectiveNameSheddable,
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: true},
-			wantErrCode:            errutil.InferencePoolResourceExhausted,
+			inferenceObjectiveName:  objectiveNameSheddable,
+			mockAdmissionController: &mockAdmissionController{admitErr: errutil.Error{Code: errutil.InferencePoolResourceExhausted, Msg: "simulated admission rejection"}},
+			wantErrCode:             errutil.InferencePoolResourceExhausted,
 		},
 		{
-			name:                   "model not found, expect err",
-			reqBodyMap:             map[string]any{"prompt": "p"},
-			mockSaturationDetector: &mockSaturationDetector{isSaturated: false},
-			wantErrCode:            errutil.BadRequest,
+			name:                    "model not found, expect err",
+			reqBodyMap:              map[string]any{"prompt": "p"},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
+			wantErrCode:             errutil.BadRequest,
 		},
-
 		{
 			name:        "prompt or messages not found, expect err",
 			reqBodyMap:  map[string]any{"model": model},
@@ -383,6 +369,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 				"model":  model,
 				"prompt": "prompt that causes scheduler error",
 			},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleErr = errors.New("simulated scheduler failure")
 			},
@@ -395,6 +382,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 				"model":  model,
 				"prompt": "prompt for nil,nil scheduler return",
 			},
+			mockAdmissionController: &mockAdmissionController{admitErr: nil},
 			schedulerMockSetup: func(m *mockScheduler) {
 				m.scheduleResults = nil
 				m.scheduleErr = nil
@@ -410,7 +398,7 @@ func TestDirector_HandleRequest(t *testing.T) {
 			if test.schedulerMockSetup != nil {
 				test.schedulerMockSetup(mockSched)
 			}
-			director := NewDirectorWithConfig(ds, mockSched, test.mockSaturationDetector, NewConfig())
+			director := NewDirectorWithConfig(ds, mockSched, test.mockAdmissionController, NewConfig())
 
 			reqCtx := &handlers.RequestContext{
 				Request: &handlers.Request{
@@ -529,7 +517,7 @@ func TestGetCandidatePodsForScheduling(t *testing.T) {
 	ds := &mockDatastore{pods: testInput}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			director := NewDirectorWithConfig(ds, &mockScheduler{}, &mockSaturationDetector{}, NewConfig())
+			director := NewDirectorWithConfig(ds, &mockScheduler{}, &mockAdmissionController{}, NewConfig())
 
 			got := director.getCandidatePodsForScheduling(context.Background(), test.metadata)
 
@@ -598,7 +586,7 @@ func TestDirector_HandleResponse(t *testing.T) {
 	ctx := logutil.NewTestLoggerIntoContext(context.Background())
 	ds := datastore.NewDatastore(t.Context(), nil)
 	mockSched := &mockScheduler{}
-	director := NewDirectorWithConfig(ds, mockSched, nil, NewConfig().WithPostResponsePlugins(pr1))
+	director := NewDirectorWithConfig(ds, mockSched, &mockAdmissionController{}, NewConfig().WithPostResponsePlugins(pr1))
 
 	reqCtx := &handlers.RequestContext{
 		Request: &handlers.Request{
diff --git a/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer_test.go b/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer_test.go
index 6d4fcc5..c35af8e 100644
--- a/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer_test.go
+++ b/pkg/epp/scheduling/framework/plugins/multi/prefix/indexer_test.go
@@ -46,3 +46,63 @@ func TestIndexer_AddAndGet(t *testing.T) {
 	servers = i.Get(BlockHash(4))
 	assert.Empty(t, servers, "Cache should not contain non-existent hash")
 }
+
+func TestIndexer_RemovePodAndEviction(t *testing.T) {
+	const indexerSize = 10
+
+	i := newIndexer(context.Background(), indexerSize)
+
+	server1 := ServerID{Namespace: "default", Name: "server1"}
+	server2 := ServerID{Namespace: "default", Name: "server2"}
+
+	// Add indexerSize hashes to both servers
+	var hashes []BlockHash
+	for j := 0; j < indexerSize; j++ {
+		h := BlockHash(j)
+		hashes = append(hashes, h)
+		i.Add([]BlockHash{h}, server1)
+		i.Add([]BlockHash{h}, server2)
+	}
+
+	// Ensure all entries are added
+	assert.Equal(t, indexerSize, i.podToLRU[server1].Len(), "server1 should have 10 entries")
+	assert.Equal(t, indexerSize, i.podToLRU[server2].Len(), "server2 should have 10 entries")
+
+	// Ensure each hash in hashToPods maps to both server1 and server2
+	for _, h := range hashes {
+		pods := i.hashToPods[h]
+		assert.Len(t, pods, 2, "Each hash should be associated with exactly 2 pods")
+		assert.Contains(t, pods, server1, "hash should be associated with server1")
+		assert.Contains(t, pods, server2, "hash should be associated with server2")
+	}
+
+	// Add indexerSize hash to server1 → should evict BlockHash(0)
+	evictedHash := BlockHash(0)
+	newHash := BlockHash(indexerSize)
+	i.Add([]BlockHash{newHash}, server1)
+
+	// server1 LRU should still be at max capacity
+	assert.Equal(t, indexerSize, i.podToLRU[server1].Len(), "server1 LRU should maintain max size")
+
+	// BlockHash(0) should no longer have server1 in hashToPods
+	pods := i.Get(evictedHash)
+	assert.NotContains(t, pods, server1, "server1 should be evicted from hashToPods for hash 0")
+	assert.Contains(t, pods, server2, "server2 should still have hash 0")
+
+	// Remove server2
+	i.RemovePod(server2)
+
+	// hashToPods for hash 0 should now be empty
+	pods = i.Get(evictedHash)
+	assert.NotContains(t, pods, server2, "server2 should be removed from hash 0")
+	assert.Empty(t, pods, "hash 0 should have no pods after both eviction and removal")
+
+	// All remaining hashes should map only to server1
+	for hash, pods := range i.hashToPods {
+		assert.Len(t, pods, 1, "hash %v should have only 1 pod after server2 removal", hash)
+		assert.Contains(t, pods, server1, "hash %v should only contain server1", hash)
+	}
+
+	// Ensure hashToPods contains exactly indexerSize hashes (post-eviction and server2 removal)
+	assert.Len(t, i.hashToPods, indexerSize, "hashToPods should contain %d hashes after cleanup", indexerSize)
+}
diff --git a/test/integration/epp/hermetic_test.go b/test/integration/epp/hermetic_test.go
index 3dc42f8..c2e100f 100644
--- a/test/integration/epp/hermetic_test.go
+++ b/test/integration/epp/hermetic_test.go
@@ -1180,7 +1180,8 @@ func BeforeSuite() func() {
 	}
 	detector := saturationdetector.NewDetector(sdConfig, logger.WithName("saturation-detector"))
 	serverRunner.SaturationDetector = detector
-	serverRunner.Director = requestcontrol.NewDirectorWithConfig(serverRunner.Datastore, scheduler, detector, requestcontrol.NewConfig())
+	admissionController := requestcontrol.NewLegacyAdmissionController(detector)
+	serverRunner.Director = requestcontrol.NewDirectorWithConfig(serverRunner.Datastore, scheduler, admissionController, requestcontrol.NewConfig())
 	serverRunner.SecureServing = false
 
 	if err := serverRunner.SetupWithManager(context.Background(), mgr); err != nil {
diff --git a/test/utils/handle.go b/test/utils/handle.go
index 4a29dda..273539f 100644
--- a/test/utils/handle.go
+++ b/test/utils/handle.go
@@ -19,6 +19,7 @@ package utils
 import (
 	"context"
 
+	backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
 )
 
@@ -33,6 +34,10 @@ func (h *testHandle) Context() context.Context {
 	return h.ctx
 }
 
+func (h *testHandle) PodList(predicate func(backendmetrics.PodMetrics) bool) []backendmetrics.PodMetrics {
+	return []backendmetrics.PodMetrics{}
+}
+
 type testHandlePlugins struct {
 	plugins map[string]plugins.Plugin
 }