Repository: kubevela/kubevela

Issue:
Test: Refactor and Enhance Unit Tests for `pkg/appfile` and `pkg/component`
**Goal:** To improve the test coverage, reliability, and maintainability of the `pkg/appfile` package. This is achieved by migrating the existing test suite from the Ginkgo framework to the standard `testing` package with `testify` and by adding a comprehensive set of new unit tests.

**Affected Package:** 
- `pkg/appfile`
- `pkg/component`

**Key Contributions:**

*   **Test Framework Migration:** The entire test suite was refactored to remove the dependency on Ginkgo and Gomega. All tests now use `testify/assert` for assertions and standard `t.Run` for sub-tests, improving consistency with the rest of the codebase.

*   **Enhanced Test Coverage:** Added extensive new unit tests for critical `appfile` logic that previously had gaps in coverage. This includes:
    *   **Parsing:** Comprehensive tests for policy parsing, legacy application revision handling, and dynamic component loading.
    *   **Manifest Generation:** New tests for `GenerateComponentManifests` and `GeneratePolicyManifests` to ensure the correctness of generated Kubernetes resources.
    *   **OAM Contracts:** Coverage for `SetOAMContract` and `setWorkloadRefToTrait` to verify the proper injection of OAM labels and workload references.
    *   **Template & Context:** Tests for loading templates from application revisions (`LoadTemplateFromRevision`) and the logic for preparing the process context (`PrepareProcessContext`).
    *   **Validation:** More robust validation tests for component parameters and ensuring the uniqueness of output resource names.
    *   **Component Test Refactoring:** Migrated `pkg/component/ref-objects` tests to standard Go testing and added new unit test cases.

**Benefits:**

*   **Increased Reliability:** The new tests provide a stronger safety net, ensuring the stability of the core application file parsing and processing logic.
*   **Improved Maintainability:** By removing the Ginkgo dependency and aligning with project-standard testing patterns, the test suite is now easier to read, maintain, and extend.
*   **Simplified Dependencies:** Reduces the number of testing-related dependencies for the project.

Hints from issue comments:

Solution patch:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 16c3e7aa..087d1d9d 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,35 +1,35 @@
 # This file is a github code protect rule follow the codeowners https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file
 
-*                                  @barnettZQG @wonderflow @leejanee @Somefive @jefree-cat @FogDong @wangyikewxgm @chivalryq @anoop2811
-design/                            @barnettZQG @leejanee @wonderflow @Somefive @jefree-cat @FogDong @anoop2811
+*                                  @barnettZQG @wonderflow @leejanee @Somefive @jefree-cat @FogDong @wangyikewxgm @chivalryq @anoop2811 @briankane @jguionnet
+design/                            @barnettZQG @leejanee @wonderflow @Somefive @jefree-cat @FogDong @anoop2811 @briankane @jguionnet
 
 # Owner of Core Controllers
-pkg/controller/core.oam.dev       @Somefive @FogDong @barnettZQG @wonderflow @wangyikewxgm @chivalryq @anoop2811
+pkg/controller/core.oam.dev       @Somefive @FogDong @barnettZQG @wonderflow @wangyikewxgm @chivalryq @anoop2811 @briankane @jguionnet
 
 # Owner of Standard Controllers
-pkg/controller/standard.oam.dev   @wangyikewxgm @barnettZQG @wonderflow @Somefive @anoop2811 @FogDong
+pkg/controller/standard.oam.dev   @wangyikewxgm @barnettZQG @wonderflow @Somefive @anoop2811 @FogDong @briankane @jguionnet
 
 # Owner of CUE
-pkg/cue                            @leejanee @FogDong @Somefive @anoop2811
-pkg/stdlib                         @leejanee @FogDong @Somefive @anoop2811
+pkg/cue                            @leejanee @FogDong @Somefive @anoop2811 @briankane @jguionnet
+pkg/stdlib                         @leejanee @FogDong @Somefive @anoop2811 @briankane @jguionnet
 
 # Owner of Workflow
-pkg/workflow                       @leejanee @FogDong @Somefive @wangyikewxgm @chivalryq @anoop2811
+pkg/workflow                       @leejanee @FogDong @Somefive @wangyikewxgm @chivalryq @anoop2811 @briankane @jguionnet
 
 # Owner of vela templates
-vela-templates/                     @Somefive @barnettZQG @wonderflow @FogDong @wangyikewxgm @chivalryq @anoop2811
+vela-templates/                     @Somefive @barnettZQG @wonderflow @FogDong @wangyikewxgm @chivalryq @anoop2811 @briankane @jguionnet
 
 # Owner of vela CLI
-references/cli/                     @Somefive @StevenLeiZhang @charlie0129 @wangyikewxgm @chivalryq @anoop2811 @FogDong
+references/cli/                     @Somefive @StevenLeiZhang @charlie0129 @wangyikewxgm @chivalryq @anoop2811 @FogDong @briankane @jguionnet
 
 # Owner of vela addon framework
-pkg/addon/                         @wangyikewxgm @wonderflow @charlie0129 @anoop2811 @FogDong
+pkg/addon/                         @wangyikewxgm @wonderflow @charlie0129 @anoop2811 @FogDong @briankane @jguionnet
 
 # Owner of resource keeper and tracker
-pkg/resourcekeeper                 @Somefive @FogDong @chivalryq @anoop2811
-pkg/resourcetracker                @Somefive @FogDong @chivalryq @anoop2811
+pkg/resourcekeeper                 @Somefive @FogDong @chivalryq @anoop2811 @briankane @jguionnet
+pkg/resourcetracker                @Somefive @FogDong @chivalryq @anoop2811 @briankane @jguionnet
 
-.github/                           @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811
-makefiles                          @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811
-go.*                               @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811
+.github/                           @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811 @briankane @jguionnet
+makefiles                          @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811 @briankane @jguionnet
+go.*                               @chivalryq @wonderflow @Somefive @FogDong @wangyikewxgm @anoop2811 @briankane @jguionnet
 
diff --git a/.github/actions/deploy-current-branch/README.md b/.github/actions/deploy-current-branch/README.md
new file mode 100644
index 00000000..6f97c762
--- /dev/null
+++ b/.github/actions/deploy-current-branch/README.md
@@ -0,0 +1,35 @@
+# Deploy Current Branch Action
+
+This GitHub composite action builds a Docker image from the current branch commit and deploys it to a KubeVela cluster for development testing.
+
+## What it does
+
+- Generates a unique image tag from the latest commit hash
+- Builds and loads the Docker image into a KinD cluster
+- Applies KubeVela CRDs for upgrade safety
+- Upgrades the KubeVela Helm release to use the local development image
+- Verifies deployment status and the running image version
+
+## Usage
+
+```yaml
+- name: Deploy Current Branch
+  uses: ./path/to/this/action
+```
+
+## Requirements
+
+- Docker, Helm, kubectl, and KinD must be available in your runner environment
+- Kubernetes cluster access
+- `charts/vela-core/crds` directory with CRDs
+- Valid Helm chart at `charts/vela-core`
+
+## Steps performed
+
+1. **Generate commit hash for image tag**
+2. **Build & load Docker image into KinD**
+3. **Pre-apply chart CRDs**
+4. **Upgrade KubeVela using local image**
+5. **Verify deployment and image version**
+
+---
\ No newline at end of file
diff --git a/.github/actions/deploy-current-branch/action.yaml b/.github/actions/deploy-current-branch/action.yaml
new file mode 100644
index 00000000..5e296cd4
--- /dev/null
+++ b/.github/actions/deploy-current-branch/action.yaml
@@ -0,0 +1,89 @@
+name: 'Deploy Current Branch'
+description: 'Builds Docker image from current branch commit and deploys it to KubeVela cluster for development testing'
+
+runs:
+  using: "composite"
+  steps:
+    # ========================================================================
+    # Git Commit Hash Generation
+    # Generate unique image tag from current branch's latest commit
+    # ========================================================================
+    - name: Get commit hash
+      id: commit_hash
+      shell: bash
+      run: |
+        COMMIT_HASH="git-$(git rev-parse --short HEAD)"
+        echo "Using commit hash: $COMMIT_HASH"
+        echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV
+
+    # ========================================================================
+    # Docker Image Build and Cluster Loading
+    # Build development image from current code and load into KinD cluster
+    # ========================================================================
+    - name: Build and load Docker image
+      shell: bash
+      run: |
+        echo "Building development image: vela-core-test:${{ env.COMMIT_HASH }}"
+
+        mkdir -p $HOME/tmp/
+
+        docker build --no-cache \
+          -t vela-core-test:${{ env.COMMIT_HASH }} \
+          -f Dockerfile .
+
+        echo "Loading image into KinD cluster..."
+        TMPDIR=$HOME/tmp/ kind load docker-image vela-core-test:${{ env.COMMIT_HASH }}
+
+    # ========================================================================
+    # Custom Resource Definitions Application
+    # Pre-apply CRDs to ensure upgrade compatibility and prevent conflicts
+    # ========================================================================
+    - name: Pre-apply CRDs from target chart (upgrade-safe)
+      shell: bash
+      run: |
+        CRD_DIR="charts/vela-core/crds"
+
+        echo "Applying CRDs idempotently..."
+        kubectl apply -f "${CRD_DIR}"
+
+    # ========================================================================
+    # KubeVela Helm Chart Upgrade
+    # Upgrade existing installation to use locally built development image
+    # ========================================================================
+    - name: Upgrade KubeVela to development image
+      shell: bash
+      run: |
+        echo "Upgrading KubeVela to development version..."
+        helm upgrade kubevela ./charts/vela-core \
+          --namespace vela-system \
+          --set image.repository=vela-core-test \
+          --set image.tag=${{ env.COMMIT_HASH }} \
+          --set image.pullPolicy=IfNotPresent \
+          --timeout 5m \
+          --wait \
+          --debug
+
+    # ========================================================================
+    # Deployment Status Verification
+    # Verify successful upgrade and confirm correct image deployment
+    # ========================================================================
+    - name: Verify deployment status
+      shell: bash
+      run: |
+        echo "=== DEPLOYMENT VERIFICATION ==="
+        echo "Verifying upgrade to local development image..."
+
+        echo "--- Pod Status ---"
+        kubectl get pods -n vela-system
+
+        echo "--- Deployment Rollout ---"
+        kubectl rollout status deployment/kubevela-vela-core \
+          -n vela-system \
+          --timeout=300s
+
+        echo "--- Deployed Image Version ---"
+        kubectl get deployment kubevela-vela-core \
+          -n vela-system \
+          -o yaml | grep "image:" | head -1
+
+        echo "Deployment verification completed successfully!"
\ No newline at end of file
diff --git a/.github/actions/deploy-latest-release/README.md b/.github/actions/deploy-latest-release/README.md
new file mode 100644
index 00000000..4eabbc39
--- /dev/null
+++ b/.github/actions/deploy-latest-release/README.md
@@ -0,0 +1,32 @@
+# Install Latest KubeVela Release Action
+
+This GitHub composite action installs the latest stable KubeVela release from the official Helm repository and verifies its deployment status.
+
+## What it does
+
+- Discovers the latest stable KubeVela release tag from GitHub
+- Adds and updates the official KubeVela Helm chart repository
+- Installs KubeVela into the `vela-system` namespace (using Helm)
+- Verifies pod status and deployment rollout for successful installation
+
+## Usage
+
+```yaml
+- name: Install Latest KubeVela Release
+  uses: ./path/to/this/action
+```
+
+## Requirements
+
+- Helm, kubectl, jq, and curl must be available in your runner environment
+- Kubernetes cluster access
+
+## Steps performed
+
+1. **Release Tag Discovery:** Fetches latest stable tag (without `v` prefix)
+2. **Helm Repo Setup:** Adds/updates KubeVela Helm chart repo
+3. **Install KubeVela:** Installs latest release in the `vela-system` namespace
+4. **Status Verification:** Checks pod status and rollout for readiness
+
+---
+
diff --git a/.github/actions/deploy-latest-release/action.yaml b/.github/actions/deploy-latest-release/action.yaml
new file mode 100644
index 00000000..05e0115e
--- /dev/null
+++ b/.github/actions/deploy-latest-release/action.yaml
@@ -0,0 +1,68 @@
+name: 'Install Latest KubeVela Release'
+description: 'Installs the latest stable KubeVela release from official Helm repository with status verification'
+
+runs:
+  using: "composite"
+  steps:
+    # ========================================================================
+    # Latest Release Tag Discovery
+    # Fetch current stable release version from GitHub API
+    # ========================================================================
+    - name: Get latest KubeVela release tag (no v prefix)
+      id: get_latest_tag
+      shell: bash
+      run: |
+        TAG=$(curl -s https://api.github.com/repos/kubevela/kubevela/releases/latest | \
+              jq -r ".tag_name" | \
+              awk '{sub(/^v/, ""); print}')
+        echo "LATEST_TAG=$TAG" >> $GITHUB_ENV
+        echo "Discovered latest release: $TAG"
+
+    # ========================================================================
+    # Helm Repository Configuration
+    # Add and update official KubeVela chart repository
+    # ========================================================================
+    - name: Add KubeVela Helm repo
+      shell: bash
+      run: |
+        echo "Adding KubeVela Helm repository..."
+        helm repo add kubevela https://kubevela.github.io/charts
+        helm repo update
+        echo "Helm repository configuration completed"
+
+    # ========================================================================
+    # KubeVela Stable Release Installation
+    # Deploy latest stable version to vela-system namespace
+    # ========================================================================
+    - name: Install KubeVela ${{ env.LATEST_TAG }}
+      shell: bash
+      run: |
+        echo "Installing KubeVela version: ${{ env.LATEST_TAG }}"
+        helm install \
+          --create-namespace \
+          -n vela-system \
+          kubevela kubevela/vela-core \
+          --version ${{ env.LATEST_TAG }} \
+          --timeout 10m \
+          --wait
+        echo "KubeVela installation completed"
+
+    # ========================================================================
+    # Installation Status Verification
+    # Verify successful deployment and readiness of KubeVela components
+    # ========================================================================
+    - name: Post-install status
+      shell: bash
+      run: |
+        echo "=== INSTALLATION VERIFICATION ==="
+        echo "Verifying KubeVela deployment status..."
+
+        echo "--- Pod Status ---"
+        kubectl get pods -n vela-system
+
+        echo "--- Deployment Rollout ---"
+        kubectl rollout status deployment/kubevela-vela-core \
+          -n vela-system \
+          --timeout=300s
+
+        echo "KubeVela installation verification completed successfully!"
\ No newline at end of file
diff --git a/.github/actions/env-setup/README.md b/.github/actions/env-setup/README.md
new file mode 100644
index 00000000..a4cd04d2
--- /dev/null
+++ b/.github/actions/env-setup/README.md
@@ -0,0 +1,67 @@
+# Kubevela Test Environment Setup Action
+
+A GitHub Actions composite action that sets up a complete testing environment for Kubevela projects with Go, Kubernetes tools, and the Ginkgo testing framework.
+
+## Features
+
+- 🛠️ **System Dependencies**: Installs essential build tools (make, gcc, jq, curl, etc.)
+- ☸️ **Kubernetes Tools**: Sets up kubectl and Helm for cluster operations
+- 🐹 **Go Environment**: Configurable Go version with module caching
+- 📦 **Dependency Management**: Downloads and verifies Go module dependencies
+- 🧪 **Testing Framework**: Installs Ginkgo v2 for BDD-style testing
+
+## Usage
+
+```yaml
+- name: Setup Kubevela Test Environment
+  uses: ./path/to/this/action
+  with:
+    go-version: '1.23.8'      # Optional: Go version (default: 1.23.8)
+```
+
+### Example Workflow
+
+```yaml
+name: Kubevela Tests
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      
+      - name: Setup Test Environment
+        uses: ./path/to/this/action
+        with:
+          go-version: '1.21'
+          
+      - name: Run Tests
+        run: |
+          ginkgo -r ./tests/e2e/
+```
+
+## Inputs
+
+| Input | Description | Required | Default | Usage |
+|-------|-------------|----------|---------|-------|
+| `go-version` | Go version to install and use | No | `1.23.8` | Specify Go version for your project |
+
+## What This Action Installs
+
+### System Tools
+- **make**: Build automation tool
+- **gcc**: GNU Compiler Collection
+- **jq**: JSON processor for shell scripts
+- **ca-certificates**: SSL/TLS certificates
+- **curl**: HTTP client for downloads
+- **gnupg**: GNU Privacy Guard for security
+
+### Kubernetes Ecosystem
+- **kubectl**: Kubernetes command-line tool (latest stable)
+- **helm**: Kubernetes package manager (latest stable)
+
+### Go Development
+- **Go Runtime**: Specified version with module caching enabled
+- **Go Modules**: Downloaded and verified dependencies
+- **Ginkgo v2.14.0**: BDD testing framework for Go
\ No newline at end of file
diff --git a/.github/actions/env-setup/action.yaml b/.github/actions/env-setup/action.yaml
new file mode 100644
index 00000000..c41a6158
--- /dev/null
+++ b/.github/actions/env-setup/action.yaml
@@ -0,0 +1,72 @@
+name: 'Kubevela Test Environment Setup'
+description: 'Sets up complete testing environment for Kubevela with Go, Kubernetes tools, and Ginkgo framework for E2E testing.'
+
+inputs:
+  go-version:
+    description: 'Go version to use for testing'
+    required: false
+    default: '1.23.8'
+
+
+runs:
+  using: 'composite'
+  steps:
+    # ========================================================================
+    # Environment Setup
+    # ========================================================================
+    - name: Install system dependencies
+      shell: bash
+      run: |
+        # Update package manager and install essential tools
+        sudo apt-get update
+        sudo apt-get install -y \
+          make \
+          gcc \
+          jq \
+          ca-certificates \
+          curl \
+          gnupg
+
+    - name: Install kubectl and helm
+      shell: bash
+      run: |
+        # Detect architecture
+        ARCH=$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')
+        
+        # Install kubectl
+        echo "Installing kubectl for architecture: $ARCH"
+        curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/${ARCH}/kubectl"
+        chmod +x kubectl
+        sudo mv kubectl /usr/local/bin/
+        
+        # Install helm using the official script (more reliable)
+        echo "Installing Helm using official script..."
+        curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
+        chmod 700 get_helm.sh
+        ./get_helm.sh
+        rm get_helm.sh
+        
+        # Verify installations
+        echo "Verifying installations..."
+        kubectl version --client
+        helm version
+    
+
+    - name: Setup Go environment
+      uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
+      with:
+        go-version: ${{ inputs.go-version }}
+        cache: true
+
+    - name: Download Go dependencies
+      shell: bash
+      run: |
+        # Download and cache Go module dependencies
+        go mod download
+        go mod verify
+
+    - name: Install Ginkgo testing framework
+      shell: bash
+      run: |
+        echo "Installing Ginkgo testing framework..."
+        go install github.com/onsi/ginkgo/v2/ginkgo@v2.14.0
diff --git a/.github/actions/setup-kind-cluster/README.md b/.github/actions/setup-kind-cluster/README.md
new file mode 100644
index 00000000..aaf70982
--- /dev/null
+++ b/.github/actions/setup-kind-cluster/README.md
@@ -0,0 +1,78 @@
+# Setup Kind Cluster Action
+
+A GitHub Action that sets up a Kubernetes testing environment using Kind (Kubernetes in Docker) for E2E testing.
+
+## Inputs
+
+| Input | Description | Required | Default |
+|-------|-------------|----------|---------|
+| `k8s-version` | Kubernetes version for the kind cluster | No | `v1.31.9` |
+
+## Quick Start
+
+```yaml
+name: E2E Tests
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      
+      - uses: actions/setup-go@v5
+        with:
+          go-version: '1.21'
+      
+      - name: Setup Kind Cluster
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          k8s-version: 'v1.31.9'
+      
+      - name: Run tests
+        run: |
+          kubectl cluster-info
+          make test-e2e
+```
+
+## What it does
+
+1. **Installs Kind CLI** - Downloads Kind v0.29.0 using Go
+2. **Cleans up** - Removes any existing Kind clusters
+3. **Creates cluster** - Spins up Kubernetes v1.31.9 cluster
+4. **Sets up environment** - Configures KUBECONFIG for kubectl access
+5. **Loads images** - Builds and loads Docker images using `make image-load`
+
+## File Structure
+
+Save as `.github/actions/setup-kind-cluster/action.yaml`:
+
+```yaml
+name: 'SetUp kind cluster'
+description: 'Sets up complete testing environment for Kubevela with Go, Kubernetes tools, and Ginkgo framework for E2E testing.'
+
+inputs:
+  k8s-version:
+    description: 'Kubernetes version for the kind cluster'
+    required: false
+    default: 'v1.31.9'
+
+runs:
+  using: 'composite'
+  steps:
+    # ========================================================================
+    # Kind cluster Setup
+    # ========================================================================
+    - name: Setup KinD
+      run: |
+        go install sigs.k8s.io/kind@v0.29.0
+        kind delete cluster || true
+        kind create cluster --image=kindest/node:${{ inputs.k8s-version }}
+      shell: bash
+
+    - name: Load image
+      run: |
+        mkdir -p $HOME/tmp/
+        TMPDIR=$HOME/tmp/ make image-load
+      shell: bash
+```
\ No newline at end of file
diff --git a/.github/actions/setup-kind-cluster/action.yaml b/.github/actions/setup-kind-cluster/action.yaml
new file mode 100644
index 00000000..824c7828
--- /dev/null
+++ b/.github/actions/setup-kind-cluster/action.yaml
@@ -0,0 +1,36 @@
+name: 'SetUp kind cluster'
+description: 'Sets up a KinD (Kubernetes in Docker) cluster with configurable Kubernetes version and optional cluster naming for testing and development workflows.'
+inputs:
+  k8s-version:
+    description: 'Kubernetes version for the kind cluster'
+    required: false
+    default: 'v1.31.9'
+  name:
+    description: 'Name of the kind cluster'
+    required: false
+runs:
+  using: 'composite'
+  steps:
+    # ========================================================================
+    # Kind cluster Setup
+    # ========================================================================
+    - name: Setup KinD
+      run: |
+        go install sigs.k8s.io/kind@v0.29.0
+        if [ -n "${{ inputs.name }}" ]; then
+          kind delete cluster --name="${{ inputs.name }}" || true
+          kind create cluster --name="${{ inputs.name }}" --image=kindest/node:${{ inputs.k8s-version }}
+          kind export kubeconfig --internal --name="${{ inputs.name }}" --kubeconfig /tmp/${{ inputs.name }}.kubeconfig
+        else
+          kind delete cluster || true
+          kind create cluster --image=kindest/node:${{ inputs.k8s-version }}
+        fi
+      shell: bash
+
+    - name: Load image
+      run: |
+        if [ -z "${{ inputs.name }}" ]; then
+          mkdir -p $HOME/tmp/
+          TMPDIR=$HOME/tmp/ make image-load
+        fi
+      shell: bash
\ No newline at end of file
diff --git a/.github/comment.userlist b/.github/comment.userlist
index c3f04aff..785e8fd8 100644
--- a/.github/comment.userlist
+++ b/.github/comment.userlist
@@ -11,4 +11,7 @@ wangyuan249
 chivalryq
 FogDong
 leejanee
-barnettZQG
\ No newline at end of file
+barnettZQG
+anoop2811 
+briankane 
+jguionnet
\ No newline at end of file
diff --git a/.github/workflows/definition-lint.yml b/.github/workflows/definition-lint.yml
index 1714f5c3..bdfa61c0 100644
--- a/.github/workflows/definition-lint.yml
+++ b/.github/workflows/definition-lint.yml
@@ -33,9 +33,9 @@ jobs:
           submodules: true
 
       - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind create cluster --image=kindest/node:v1.31.9
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          name: linter
 
       - name: Definition Doc generate check
         run: |
diff --git a/.github/workflows/e2e-multicluster-test.yml b/.github/workflows/e2e-multicluster-test.yml
index b2be990c..5ac8d12a 100644
--- a/.github/workflows/e2e-multicluster-test.yml
+++ b/.github/workflows/e2e-multicluster-test.yml
@@ -44,7 +44,7 @@ jobs:
     if: needs.detect-noop.outputs.noop != 'true'
     strategy:
       matrix:
-        k8s-version: ["v1.31"]
+        k8s-version: ["v1.31.9"]
     concurrency:
       group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
       cancel-in-progress: true
@@ -53,61 +53,31 @@ jobs:
       - name: Check out code into the Go module directory
         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
 
-      - name: Install tools
-        run: |
-          sudo apt-get update
-          sudo apt-get install make gcc jq ca-certificates curl gnupg -y
-          sudo snap install kubectl --classic
-          sudo snap install helm --classic
-
-      - name: Setup Go
-        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
+      - name: Setup worker cluster kinD
+        uses: ./.github/actions/setup-kind-cluster
         with:
-          go-version: ${{ env.GO_VERSION }}
-
-      - name: Get dependencies
-        run: |
-          go get -v -t -d ./...
-
-      - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind delete cluster --name worker || true
-          kind create cluster --name worker --image=kindest/node:v1.31.9
-          kind export kubeconfig --internal --name worker --kubeconfig /tmp/worker.kubeconfig
-          kind delete cluster || true
-          kind create cluster --image=kindest/node:v1.31.9
-
-      - name: Load image
-        run: |
-          mkdir -p $HOME/tmp/ 
-          TMPDIR=$HOME/tmp/ make image-load
+          name: worker
+          k8s-version: ${{ matrix.k8s-version }}
 
-      - name: Cleanup for e2e tests
-        run: |
-          make vela-cli
-          make e2e-cleanup
-          make e2e-setup-core-auth
-
-      - name: Run e2e multicluster tests
-        run: |
-          export PATH=$(pwd)/bin:$PATH
-          make e2e-multicluster-test
-
-      - name: Stop kubevela, get profile
-        run: |
-          make end-e2e-core-shards
+      - name: Setup master cluster kinD
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          k8s-version: ${{ matrix.k8s-version }}
 
-      - name: Upload coverage report
-        uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+      - name: Run upgrade multicluster tests
+        uses: ./.github/actions/multicluster-test
         with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          files: /tmp/e2e-profile.out,/tmp/e2e_multicluster_test.out
-          flags: e2e-multicluster-test
-          name: codecov-umbrella
+          codecov-enable: true
+          codecov-token: ${{ secrets.CODECOV_TOKEN }}
 
       - name: Clean e2e profile
-        run: rm /tmp/e2e-profile.out
+        run: |
+          if [ -f /tmp/e2e-profile.out ]; then
+            rm /tmp/e2e-profile.out
+            echo "E2E profile cleaned"
+          else
+            echo "E2E profile not found, skipping cleanup"
+          fi
 
       - name: Cleanup image
         if: ${{ always() }}
diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml
index de063b15..ee46361a 100644
--- a/.github/workflows/e2e-test.yml
+++ b/.github/workflows/e2e-test.yml
@@ -53,68 +53,26 @@ jobs:
       - name: Check out code into the Go module directory
         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
 
-      - name: Install tools
-        run: |
-          sudo apt-get update
-          sudo apt-get install make gcc jq ca-certificates curl gnupg -y
-          sudo snap install kubectl --classic
-          sudo snap install helm --classic
-
-      - name: Setup Go
-        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
-        with:
-          go-version: ${{ env.GO_VERSION }}
-
-      - name: Get dependencies
-        run: |
-          go get -v -t -d ./...
-
       - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind delete cluster || true
-          kind create cluster --image=kindest/node:v1.31.9
+        uses: ./.github/actions/setup-kind-cluster
 
-      - name: Get Ginkgo
-        run: |
-          go install github.com/onsi/ginkgo/v2/ginkgo@v2.14.0
-
-      - name: Load image
-        run: |
-          mkdir -p $HOME/tmp/
-          TMPDIR=$HOME/tmp/ make image-load
-
-      - name: Run Make
-        run: make
-
-      - name: Prepare for e2e tests
-        run: |
-          make e2e-cleanup
-          make e2e-setup-core
-          helm test -n vela-system kubevela --timeout 5m
-
-      - name: Run api e2e tests
-        run: make e2e-api-test
-
-      - name: Run addons e2e tests
-        run: make e2e-addon-test
-
-      - name: Run e2e tests
-        run: make e2e-test
-
-      - name: Stop kubevela, get profile
-        run: make end-e2e
-
-      - name: Upload coverage report
-        uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+      # ========================================================================
+      # E2E Test Execution
+      # ========================================================================
+      - name: Run upgrade e2e tests
+        uses: ./.github/actions/e2e-test
         with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          files: /tmp/e2e-profile.out
-          flags: e2etests
-          name: codecov-umbrella
+          codecov-enable: true
+          codecov-token: ${{ secrets.CODECOV_TOKEN }}
 
       - name: Clean e2e profile
-        run: rm /tmp/e2e-profile.out
+        run: |
+          if [ -f /tmp/e2e-profile.out ]; then
+            rm /tmp/e2e-profile.out
+            echo "E2E profile cleaned"
+          else
+            echo "E2E profile not found, skipping cleanup"
+          fi
 
       - name: Cleanup image
         if: ${{ always() }}
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 1de8a4aa..1549f8c0 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -97,28 +97,16 @@ jobs:
         with:
           submodules: true
 
-      - name: Setup Go
-        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
-        with:
-          go-version: ${{ env.GO_VERSION }}
-
+      - name: Setup Env
+        uses: ./.github/actions/env-setup
+        
       - name: Setup node
         uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
         with:
           node-version: "14"
 
-      - name: Cache Go Dependencies
-        uses: actions/cache@v4
-        with:
-          path: .work/pkg
-          key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
-          restore-keys: ${{ runner.os }}-pkg-
-
-      - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind delete cluster --name kind || true
-          kind create cluster --name kind --image=kindest/node:v1.31.9 --kubeconfig ~/.kube/config
+      - name: Setup kinD
+        uses: ./.github/actions/setup-kind-cluster
 
       - name: Run cross-build
         run: make cross-build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 078126ca..e13e73fa 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,7 +3,7 @@ name: Release
 on:
   push:
     tags:
-      - 'v*'
+      - "v*"
   workflow_dispatch: {}
 
 permissions:
@@ -26,6 +26,32 @@ jobs:
     outputs:
       hashes: ${{ steps.hash.outputs.hashes }}
     steps:
+      - name: Check disk (before)
+        run: |
+          df -h
+          sudo du -sh /usr/local/lib/android /usr/share/dotnet /opt/ghc || true
+
+      - name: Free Disk Space (Ubuntu)
+        uses: insightsengineering/disk-space-reclaimer@v1
+        with:
+          # this might remove tools that are actually needed,
+          # if set to "true" but frees about 6 GB
+          tools-cache: false
+          # all of these default to true, but feel free to set to
+          # "false" if necessary for your workflow
+          android: true
+          dotnet: true
+          haskell: true
+          large-packages: true
+          swap-storage: true
+          docker-images: true
+
+      # Extra prune in case your job builds/pulls images
+      - name: Deep Docker prune
+        run: |
+          docker system prune -af || true
+          docker builder prune -af || true
+
       - name: Checkout
         uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
         with:
@@ -43,7 +69,7 @@ jobs:
       - name: Install Cosign
         uses: sigstore/cosign-installer@main
         with:
-          cosign-release: 'v2.5.0'
+          cosign-release: "v2.5.0"
 
       - name: Install syft
         uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
@@ -65,6 +91,9 @@ jobs:
           HASHES=$(find dist -type f -exec sha256sum {} \; | base64 -w0)
           echo "hashes=$HASHES" >> "$GITHUB_OUTPUT"
 
+      - name: Check disk (after)
+        run: df -h
+
   upload-plugin-homebrew:
     name: upload-sha256sums
     needs: goreleaser
@@ -82,16 +111,9 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
+
       - name: Update kubectl plugin version in krew-index
         uses: rajatjindal/krew-release-bot@df3eb197549e3568be8b4767eec31c5e8e8e6ad8 # v0.0.46
-      - name: Update Homebrew formula
-        uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0
-        with:
-          token: ${{ secrets.HOMEBREW_TOKEN }}
-          formula: kubevela
-          tag: ${{ github.ref }}
-          revision: ${{ github.sha }}
-          force: false
 
   provenance-vela-bins:
     name: generate provenance for binaries
@@ -103,5 +125,5 @@ jobs:
       actions: read
     uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 # has to be sem var
     with:
-      base64-subjects: '${{ needs.goreleaser.outputs.hashes }}'
+      base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
       upload-assets: true
diff --git a/.github/workflows/sdk-test.yml b/.github/workflows/sdk-test.yml
index 02cbd632..b150d7c1 100644
--- a/.github/workflows/sdk-test.yml
+++ b/.github/workflows/sdk-test.yml
@@ -16,11 +16,6 @@ on:
 permissions:
   contents: read
 
-env:
-  # Common versions
-  GO_VERSION: '1.23.8'
-  GOLANGCI_VERSION: 'v1.60.1'
-
 jobs:
   sdk-tests:
     runs-on: ubuntu-22.04
@@ -28,10 +23,8 @@ jobs:
       - name: Check out code into the Go module directory
         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
 
-      - name: Setup Go
-        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
-        with:
-          go-version: ${{ env.GO_VERSION }}
+      - name: Setup Env
+        uses: ./.github/actions/env-setup
 
       - name: Install Go tools
         run: |
@@ -39,9 +32,9 @@ jobs:
           make golangci
 
       - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind create cluster --image=kindest/node:v1.31.9
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          name: sdk-test
 
       - name: Build CLI
         run: make vela-cli
diff --git a/.github/workflows/sync-api.yml b/.github/workflows/sync-api.yml
index 359d92e4..f5f93877 100644
--- a/.github/workflows/sync-api.yml
+++ b/.github/workflows/sync-api.yml
@@ -10,21 +10,16 @@ on:
 permissions:
   contents: read
 
-env:
-  GO_VERSION: '1.23.8'
-
 jobs:
   sync-core-api:
     runs-on: ubuntu-22.04
     steps:
-      - name: Set up Go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
-        with:
-          go-version: ${{ env.GO_VERSION }}
-
       - name: Check out code into the Go module directory
         uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
 
+      - name: Setup Env
+        uses: ./.github/actions/env-setup
+
       - name: Get the version
         id: get_version
         run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
diff --git a/.github/workflows/sync-sdk.yaml b/.github/workflows/sync-sdk.yaml
index ef21596a..64738572 100644
--- a/.github/workflows/sync-sdk.yaml
+++ b/.github/workflows/sync-sdk.yaml
@@ -14,36 +14,33 @@ on:
 permissions:
   contents: read
 
-env:
-  GO_VERSION: '1.23.8'
-
 jobs:
   sync_sdk:
     runs-on: ubuntu-22.04
     steps:
-      - name: Set up Go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
-        with:
-          go-version: ${{ env.GO_VERSION }}
 
       - name: Check out code into the Go module directory
         uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
 
-      - name: Get the version
-        id: get_version
-        run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
-
-      - name: Get dependencies
-        run: |
-          go get -v -t -d ./...
+      - name: Env setup
+        uses: ./.github/actions/env-setup
 
       - name: Install Go tools
         run: |
           make goimports
-          
+
       - name: Build CLI
         run: make vela-cli
 
+      - name: Setup KinD
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          name: sync-sdk
+
+      - name: Get the version
+        id: get_version
+        run: echo "VERSION=${GITHUB_REF}" >> $GITHUB_OUTPUT
+
       - name: Sync SDK to kubevela/kubevela-go-sdk
         run: bash ./hack/sdk/sync.sh
         env:
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index ed78a19b..4e24a315 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -14,10 +14,6 @@ on:
 permissions:
   contents: read
 
-env:
-  # Common versions
-  GO_VERSION: "1.23.8"
-
 jobs:
   detect-noop:
     permissions:
@@ -41,41 +37,19 @@ jobs:
     if: needs.detect-noop.outputs.noop != 'true'
 
     steps:
-      - name: Set up Go
-        uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
-        with:
-          go-version: ${{ env.GO_VERSION }}
-
       - name: Check out code into the Go module directory
         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
         with:
           submodules: true
 
-      - name: Cache Go Dependencies
-        uses: actions/cache@v4
-        with:
-          path: .work/pkg
-          key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
-          restore-keys: ${{ runner.os }}-pkg-
-
-      - name: Install ginkgo
-        run: |
-          sudo sed -i 's/azure\.//' /etc/apt/sources.list
-          sudo apt-get update
-          sudo apt-get install -y golang-ginkgo-dev
-
-      - name: Setup KinD
-        run: |
-          go install sigs.k8s.io/kind@v0.29.0
-          kind create cluster --image=kindest/node:v1.31.9
+      - name: Setup Env
+        uses: ./.github/actions/env-setup
 
-      - name: Run Make test
-        run: make test
+      - name: Setup KinD with Kubernetes
+        uses: ./.github/actions/setup-kind-cluster
 
-      - name: Upload coverage report
-        uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 #v5.4.3
+      - name: Run unit tests
+        uses: ./.github/actions/unit-test
         with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          files: ./coverage.txt
-          flags: core-unittests
-          name: codecov-umbrella
+          codecov-enable: true
+          codecov-token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/upgrade-e2e-multicluster-test.yml b/.github/workflows/upgrade-e2e-multicluster-test.yml
new file mode 100644
index 00000000..b120efac
--- /dev/null
+++ b/.github/workflows/upgrade-e2e-multicluster-test.yml
@@ -0,0 +1,98 @@
+# =============================================================================
+# E2E Upgrade Multicluster Test Workflow
+# =============================================================================
+# This workflow performs end-to-end testing for KubeVela multicluster upgrades.
+# It tests the upgrade path from the latest released version to the current
+# development branch across multiple Kubernetes versions.
+#
+# Test Flow:
+# 1. Install latest KubeVela release
+# 2. Build and upgrade to current development version
+# 3. Run multicluster e2e tests to verify functionality
+# =============================================================================
+
+name: E2E Upgrade Multicluster Test
+
+# =============================================================================
+# Trigger Configuration
+# =============================================================================
+on:
+  # Trigger on pull requests targeting main branches
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+  # Allow manual workflow execution
+  workflow_dispatch: {}
+
+# =============================================================================
+# Security Configuration
+# =============================================================================
+permissions:
+  contents: read  # Read-only access to repository contents
+
+# =============================================================================
+# Global Environment Variables
+# =============================================================================
+env:
+  GO_VERSION: '1.23.8'  # Go version for building and testing
+
+# =============================================================================
+# Job Definitions
+# =============================================================================
+jobs:
+  upgrade-multicluster-tests:
+    name: Upgrade Multicluster Tests
+    runs-on: ubuntu-22.04
+    if: startsWith(github.head_ref, 'chore/upgrade-k8s-')
+    timeout-minutes: 60  # Prevent hanging jobs
+
+    # ==========================================================================
+    # Matrix Strategy - Test against multiple Kubernetes versions
+    # ==========================================================================
+    strategy:
+      fail-fast: false  # Continue testing other versions if one fails
+      matrix:
+        k8s-version: ['v1.31.9']
+
+    # ==========================================================================
+    # Concurrency Control - Prevent overlapping runs
+    # ==========================================================================
+    concurrency:
+      group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
+      cancel-in-progress: true
+
+    steps:
+      # ========================================================================
+      # Environment Setup
+      # ========================================================================
+
+      - name: Check out repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
+
+      # ========================================================================
+      # Kubernetes Cluster Setup
+      # ========================================================================
+
+      - name: Setup worker cluster kinD
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          name: worker
+
+      - name: Setup KinD master clusters for multicluster testing
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          k8s-version: ${{ matrix.k8s-version }}
+
+      - name: Deploy latest release
+        uses: ./.github/actions/deploy-latest-release
+
+      - name: Upgrade from current branch
+        uses: ./.github/actions/deploy-current-branch
+
+      - name: Run upgarde multicluster tests
+        uses: ./.github/actions/multicluster-test
+        with:
+          codecov-enable: false
+          codecov-token: ''
\ No newline at end of file
diff --git a/.github/workflows/upgrade-e2e-test.yml b/.github/workflows/upgrade-e2e-test.yml
new file mode 100644
index 00000000..32fc3d8e
--- /dev/null
+++ b/.github/workflows/upgrade-e2e-test.yml
@@ -0,0 +1,102 @@
+# =============================================================================
+# Upgrade E2E Test Workflow
+# =============================================================================
+# This workflow performs comprehensive end-to-end testing for KubeVela upgrades.
+# It validates the upgrade path from the latest stable release to the current
+# development version by running multiple test suites including API, addon,
+# and general e2e tests.
+#
+# Test Flow:
+# 1. Install latest KubeVela release
+# 2. Build and upgrade to current development version
+# 3. Run comprehensive e2e test suites (API, addon, general)
+# 4. Validate upgrade functionality and compatibility
+# =============================================================================
+
+name: Upgrade E2E Test
+
+# =============================================================================
+# Trigger Configuration
+# =============================================================================
+on:
+  # Trigger on pull requests targeting main branches
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+  # Allow manual workflow execution
+  workflow_dispatch: {}
+
+# =============================================================================
+# Environment Variables
+# =============================================================================
+env:
+  GO_VERSION: '1.23.8'
+
+# =============================================================================
+# Security Configuration
+# =============================================================================
+permissions:
+  contents: read  # Read-only access to repository contents
+
+# =============================================================================
+# Job Definitions
+# =============================================================================
+jobs:
+  upgrade-tests:
+    name: Upgrade E2E Tests
+    runs-on: ubuntu-22.04
+    if: startsWith(github.head_ref, 'chore/upgrade-k8s-')
+    timeout-minutes: 90  # Extended timeout for comprehensive e2e testing
+
+    # ==========================================================================
+    # Matrix Strategy - Test against multiple Kubernetes versions
+    # ==========================================================================
+    strategy:
+      fail-fast: false  # Continue testing other versions if one fails
+      matrix:
+        k8s-version: ['v1.31.9']
+
+    # ==========================================================================
+    # Concurrency Control - Prevent overlapping runs
+    # ==========================================================================
+    concurrency:
+      group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
+      cancel-in-progress: true
+
+    steps:
+      # ========================================================================
+      # Repository Setup
+      # ========================================================================
+      - name: Check out code
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
+
+      # ========================================================================
+      # Kubernetes Cluster Setup
+      # ========================================================================
+      - name: Setup KinD with Kubernetes ${{ matrix.k8s-version }}
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          k8s-version: ${{ matrix.k8s-version }}
+
+      - name: Build vela CLI
+        run: make vela-cli
+
+      - name: Build kubectl-vela plugin
+        run: make kubectl-vela
+
+      - name: Install kustomize
+        run: make kustomize
+
+      - name: Deploy latest release
+        uses: ./.github/actions/deploy-latest-release
+
+      - name: Upgrade from current branch
+        uses: ./.github/actions/deploy-current-branch
+
+      # ========================================================================
+      # E2E Test Execution
+      # ========================================================================
+      - name: Run upgrade e2e tests
+        uses: ./.github/actions/e2e-test
\ No newline at end of file
diff --git a/.github/workflows/upgrade-unit-test.yml b/.github/workflows/upgrade-unit-test.yml
new file mode 100644
index 00000000..bc98a78a
--- /dev/null
+++ b/.github/workflows/upgrade-unit-test.yml
@@ -0,0 +1,83 @@
+# =============================================================================
+# Upgrade Unit Test Workflow
+# =============================================================================
+# This workflow performs unit testing for KubeVela upgrades by:
+# 1. Installing the latest stable KubeVela release
+# 2. Building and upgrading to the current development version
+# 3. Running unit tests to validate the upgrade functionality
+# =============================================================================
+
+name: Upgrade Unit Test
+
+# =============================================================================
+# Trigger Configuration
+# =============================================================================
+on:
+  # Trigger on pull requests targeting main and release branches
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+  # Allow manual workflow execution
+  workflow_dispatch: {}
+
+# =============================================================================
+# Security Configuration
+# =============================================================================
+permissions:
+  contents: read  # Read-only access to repository contents
+
+# =============================================================================
+# Job Definitions
+# =============================================================================
+jobs:
+  upgrade-tests:
+    name: Upgrade Unit Tests
+    runs-on: ubuntu-22.04
+    if: startsWith(github.head_ref, 'chore/upgrade-k8s-')
+    timeout-minutes: 45  # Prevent hanging jobs
+
+    # ==========================================================================
+    # Matrix Strategy - Test against multiple Kubernetes versions
+    # ==========================================================================
+    strategy:
+      fail-fast: false  # Continue testing other versions if one fails
+      matrix:
+        k8s-version: ['v1.31.9']
+
+    # ==========================================================================
+    # Concurrency Control - Prevent overlapping runs
+    # ==========================================================================
+    concurrency:
+      group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
+      cancel-in-progress: true
+
+    steps:
+      # ========================================================================
+      # Environment Setup
+      # ========================================================================
+
+      - name: Check out code
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
+
+      # ========================================================================
+      # Kubernetes Cluster Setup
+      # ========================================================================
+
+      - name: Setup KinD with Kubernetes ${{ matrix.k8s-version }}
+        uses: ./.github/actions/setup-kind-cluster
+        with:
+          k8s-version: ${{ matrix.k8s-version }}
+
+      - name: Deploy latest release
+        uses: ./.github/actions/deploy-latest-release
+
+      - name: Upgrade from current branch
+        uses: ./.github/actions/deploy-current-branch
+
+      - name: Run unit tests
+        uses: ./.github/actions/unit-test
+        with:
+          codecov-enable: false
+          codecov-token: ''
diff --git a/.github/workflows/webhook-upgrade-validation.yml b/.github/workflows/webhook-upgrade-validation.yml
new file mode 100644
index 00000000..5e8bceb1
--- /dev/null
+++ b/.github/workflows/webhook-upgrade-validation.yml
@@ -0,0 +1,165 @@
+name: Webhook Upgrade Validation
+
+on:
+  push:
+    branches:
+      - master
+      - release-*
+    tags:
+      - v*
+  workflow_dispatch: {}
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+permissions:
+  contents: read
+
+env:
+  GO_VERSION: '1.23.8'
+
+jobs:
+  webhook-upgrade-check:
+    runs-on: ubuntu-22.04
+    timeout-minutes: 30
+    steps:
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
+
+      - name: Setup Env
+        uses: ./.github/actions/env-setup
+
+      - name: Setup KinD
+        run: |
+          go install sigs.k8s.io/kind@v0.29.0
+          kind delete cluster || true
+          kind create cluster --image=kindest/node:v1.31.9
+
+      - name: Install KubeVela CLI
+        run: curl -fsSL https://kubevela.io/script/install.sh | bash
+
+      - name: Install KubeVela baseline
+        run: |
+          vela install --set featureGates.enableCueValidation=true
+          kubectl wait --namespace vela-system --for=condition=Available deployment/kubevela-vela-core --timeout=300s
+
+      - name: Prepare failing chart changes
+        run: |
+          cat <<'CHART' > charts/vela-core/templates/defwithtemplate/resource.yaml
+          # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
+          # Definition source cue file: vela-templates/definitions/internal/resource.cue
+          apiVersion: core.oam.dev/v1beta1
+          kind: TraitDefinition
+          metadata:
+            annotations:
+              definition.oam.dev/description: Add resource requests and limits on K8s pod for your workload which follows the pod spec in path 'spec.template.'
+            name: resource
+            namespace: {{ include "systemDefinitionNamespace" . }}
+          spec:
+            appliesToWorkloads:
+              - deployments.apps
+              - statefulsets.apps
+              - daemonsets.apps
+              - jobs.batch
+              - cronjobs.batch
+            podDisruptive: true
+            schematic:
+              cue:
+                template: |2
+                  let resourceContent = {
+                    resources: {
+                      if parameter.cpu != _|_ if parameter.memory != _|_ if parameter.requests == _|_ if parameter.limits == _|_ {
+                        // +patchStrategy=retainKeys
+                        requests: {
+                          cpu:    parameter.cpu
+                          memory: parameter.memory
+                        }
+                        // +patchStrategy=retainKeys
+                        limits: {
+                          cpu:    parameter.cpu
+                          memory: parameter.memory
+                        }
+                      }
+                      if parameter.requests != _|_ {
+                        // +patchStrategy=retainKeys
+                        requests: {
+                          cpu:    parameter.requests.cpu
+                          memory: parameter.requests.memory
+                        }
+                      }
+                      if parameter.limits != _|_ {
+                        // +patchStrategy=retainKeys
+                        limits: {
+                          cpu:    parameter.limits.cpu
+                          memory: parameter.limits.memory
+                        }
+                      }
+                    }
+                  }
+                  if context.output.spec != _|_ if context.output.spec.template != _|_ {
+                    patch: spec: template: spec: {
+                      // +patchKey=name
+                      containers: [resourceContent]
+                    }
+                  }
+                  if context.output.spec != _|_ if context.output.spec.jobTemplate != _|_ {
+                    patch: spec: jobTemplate: spec: template: spec: {
+                      // +patchKey=name
+                      containers: [resourceContent]
+                    }
+                  }
+                  parameter: {
+                    // +usage=Specify the amount of cpu for requests and limits
+                    cpu?: *1 | number | string
+                    // +usage=Specify the amount of memory for requests and limits
+                    memory?: *"2048Mi" | =~"^([1-9][0-9]{0,63})(E|P|T|G|M|K|Ei|Pi|Ti|Gi|Mi|Ki)$"
+                    // +usage=Specify the resources in requests
+                    requests?: {
+                      // +usage=Specify the amount of cpu for requests
+                      cpu: *1 | number | string
+                      // +usage=Specify the amount of memory for requests
+                      memory: *"2048Mi" | =~"^([1-9][0-9]{0,63})(E|P|T|G|M|K|Ei|Pi|Ti|Gi|Mi|Ki)$"
+                    }
+                    // +usage=Specify the resources in limits
+                    limits?: {
+                      // +usage=Specify the amount of cpu for limits
+                      cpu: *1 | number | string
+                      // +usage=Specify the amount of memory for limits
+                      memory: *"2048Mi" | =~"^([1-9][0-9]{0,63})(E|P|T|G|M|K|Ei|Pi|Ti|Gi|Mi|Ki)$"
+                    }
+                  }
+
+      - name: Load image
+        run: |
+          mkdir -p $HOME/tmp/
+          TMPDIR=$HOME/tmp/ make image-load
+
+      - name: Run Helm upgrade (expected to fail)
+        run: |
+          set +e
+          helm upgrade \
+            --set image.repository=vela-core-test \
+            --set image.tag=$(git rev-parse --short HEAD) \
+            --set featureGates.enableCueValidation=true \
+            --wait kubevela ./charts/vela-core --debug -n vela-system
+          status=$?
+          echo "Helm upgrade exit code: ${status}"
+          if [ $status -eq 0 ]; then
+            echo "Expected helm upgrade to fail" >&2
+            exit 1
+          fi
+          echo "Helm upgrade failed as expected"
+
+      - name: Dump webhook configurations
+        if: ${{ always() }}
+        run: |
+          kubectl get mutatingwebhookconfiguration kubevela-vela-core-admission -o yaml
+          kubectl get validatingwebhookconfiguration kubevela-vela-core-admission -o yaml
+
+      - name: Verify webhook validation remains active
+        run: ginkgo -v --focus-file requiredparam_validation_test.go ./test/e2e-test
+
+      - name: Cleanup kind cluster
+        if: ${{ always() }}
+        run: kind delete cluster --name kind
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 893138b4..97d51584 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,14 @@ vendor/
 .vscode
 .history
 
+# Debug binaries generated by VS Code/Delve
+__debug_bin*
+*/__debug_bin*
+
+# Webhook certificates generated at runtime
+k8s-webhook-server/
+options.go.bak 
+
 pkg/test/vela
 config/crd/bases
 _tmp/
diff --git a/app.yaml b/app.yaml
deleted file mode 100644
index e69de29b..00000000
diff --git a/charts/vela-core/README.md b/charts/vela-core/README.md
index 5f695985..8b078362 100644
--- a/charts/vela-core/README.md
+++ b/charts/vela-core/README.md
@@ -100,6 +100,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
 | `featureGates.disableWorkflowContextConfigMapCache`          | disable the workflow context's configmap informer cache                                                                                                                                                                          | `true`  |
 | `featureGates.enableCueValidation`                           | enable the strict cue validation for cue required parameter fields                                                                                                                                                               | `false` |
 | `featureGates.enableApplicationStatusMetrics`                | enable application status metrics and structured logging                                                                                                                                                                         | `false` |
+| `featureGates.validateResourcesExist`                        | enable webhook validation to check if resource types referenced in definition templates exist in the cluster                                                                                                                     | `false` |
 
 ### MultiCluster parameters
 
@@ -147,15 +148,16 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
 | `affinity`                                     | Affinity                                                                                                                                                           | `{}`                 |
 | `rbac.create`                                  | Specifies whether a RBAC role should be created                                                                                                                    | `true`               |
 | `logDebug`                                     | Enable debug logs for development purpose                                                                                                                          | `false`              |
+| `devLogs`                                      | Enable formatted logging support for development purpose                                                                                                           | `false`              |
 | `logFilePath`                                  | If non-empty, write log files in this path                                                                                                                         | `""`                 |
 | `logFileMaxSize`                               | Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited.                                         | `1024`               |
 | `kubeClient.qps`                               | The qps for reconcile clients                                                                                                                                      | `400`                |
 | `kubeClient.burst`                             | The burst for reconcile clients                                                                                                                                    | `600`                |
 | `authentication.enabled`                       | Enable authentication framework for applications                                                                                                                   | `false`              |
-| `authentication.definitions.enabled`           | Enable definition permission validation (requires authentication.enabled)                                                                                          | `false`              |
-| `authentication.withUser`                      | Application authentication will impersonate as the request User                                                                                                    | `true`               |
-| `authentication.defaultUser`                   | Application authentication will impersonate as the User if no user provided in Application                                                                         | `kubevela:vela-core` |
+| `authentication.withUser`                      | Application authentication will impersonate as the request User (must be true for security)                                                                        | `true`               |
+| `authentication.defaultUser`                   | Application authentication will impersonate as the User if no user provided or withUser is false                                                                   | `kubevela:vela-core` |
 | `authentication.groupPattern`                  | Application authentication will impersonate as the request Group that matches the pattern                                                                          | `kubevela:*`         |
+| `authorization.definitionValidationEnabled`    | Enable definition permission validation for RBAC checks on definitions                                                                                             | `false`              |
 | `sharding.enabled`                             | When sharding enabled, the controller will run as master mode. Refer to https://github.com/kubevela/kubevela/blob/master/design/vela-core/sharding.md for details. | `false`              |
 | `sharding.schedulableShards`                   | The shards available for scheduling. If empty, dynamic discovery will be used.                                                                                     | `""`                 |
 | `core.metrics.enabled`                         | Enable metrics for vela-core                                                                                                                                       | `false`              |
diff --git a/charts/vela-core/templates/NOTES.txt b/charts/vela-core/templates/NOTES.txt
index 14cb2559..9758dc8f 100644
--- a/charts/vela-core/templates/NOTES.txt
+++ b/charts/vela-core/templates/NOTES.txt
@@ -30,11 +30,35 @@ Welcome to use the KubeVela! Enjoy your shipping application journey!
 
 You can refer to https://kubevela.io for more details.
 
-{{- if and .Values.authentication.enabled (not .Values.authentication.definitions.enabled) }}
+{{- if and .Values.authentication.enabled (not .Values.authentication.withUser) }}
 
-WARNING: Authentication is enabled but definition permission validation is disabled.
-   Applications can reference definitions without RBAC checks.
-   To enable definition permission validation:
-     --set authentication.definitions.enabled=true
-   Ensure users have appropriate RBAC permissions before enabling.
+WARNING: Authentication is enabled but withUser is disabled.
+   This configuration provides NO security benefit:
+   - All applications will run as '{{ .Values.authentication.defaultUser }}' regardless of who creates them
+   - User groups matching '{{ .Values.authentication.groupPattern }}' are still collected but not used effectively
+   - Service account annotations are blocked
+   
+   To enable true user impersonation for security:
+     --set authentication.withUser=true
+{{- end }}
+
+{{- if and (not .Values.authorization.definitionValidationEnabled) (not .Values.authentication.enabled) }}
+
+SECURITY RECOMMENDATION: Both authentication and definition validation are disabled.
+   If KubeVela is running with cluster-admin or other high-level permissions,
+   consider enabling one or both security features:
+
+   1. Authentication with impersonation (recommended for multi-tenant environments):
+      --set authentication.enabled=true 
+      --set authentication.withUser=true
+      This makes KubeVela impersonate the requesting user, applying their RBAC permissions.
+      Note: Both flags must be enabled for user impersonation to work.
+
+   2. Definition permission validation (lightweight RBAC for definitions):
+      --set authorization.definitionValidationEnabled=true
+      This ensures users can only reference definitions they have access to.
+
+   Using both features together provides defense in depth.
+   Without these protections, users can leverage KubeVela's permissions to deploy
+   resources beyond their intended access level.
 {{- end }}
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/clusterrole.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/clusterrole.yaml
index 96b0ba33..446848df 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/clusterrole.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/clusterrole.yaml
@@ -4,7 +4,7 @@ kind: ClusterRole
 metadata:
   name:  {{ template "kubevela.fullname" . }}-admission
   annotations:
-    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
+    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
index 649bb713..85cc592b 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
@@ -4,7 +4,7 @@ kind: ClusterRoleBinding
 metadata:
   name:  {{ template "kubevela.fullname" . }}-admission
   annotations:
-    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
+    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
index eb2e9847..55af070f 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
@@ -5,7 +5,7 @@ metadata:
   name: {{ template "kubevela.fullname" . }}-admission-patch
   namespace: {{ .Release.Namespace }}
   annotations:
-    "helm.sh/hook": post-install,post-upgrade
+    "helm.sh/hook": post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission-patch
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/role.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/role.yaml
index cf3d7dd3..535c3221 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/role.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/role.yaml
@@ -5,7 +5,7 @@ metadata:
   name:  {{ template "kubevela.fullname" . }}-admission
   namespace: {{ .Release.Namespace }}
   annotations:
-    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
+    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/rolebinding.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/rolebinding.yaml
index cffdc0f5..84259e9c 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/rolebinding.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/rolebinding.yaml
@@ -5,7 +5,7 @@ metadata:
   name:  {{ template "kubevela.fullname" . }}-admission
   namespace: {{ .Release.Namespace }}
   annotations:
-    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
+    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission
diff --git a/charts/vela-core/templates/admission-webhooks/job-patch/serviceaccount.yaml b/charts/vela-core/templates/admission-webhooks/job-patch/serviceaccount.yaml
index 4e56267d..fe35c96c 100644
--- a/charts/vela-core/templates/admission-webhooks/job-patch/serviceaccount.yaml
+++ b/charts/vela-core/templates/admission-webhooks/job-patch/serviceaccount.yaml
@@ -5,7 +5,7 @@ metadata:
   name:  {{ template "kubevela.fullname" . }}-admission
   namespace: {{ .Release.Namespace }}
   annotations:
-    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
+    "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade,post-rollback
     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
   labels:
     app: {{ template "kubevela.name" . }}-admission
diff --git a/charts/vela-core/templates/admission-webhooks/mutatingWebhookConfiguration.yaml b/charts/vela-core/templates/admission-webhooks/mutatingWebhookConfiguration.yaml
index 73608fcb..b0b0b491 100644
--- a/charts/vela-core/templates/admission-webhooks/mutatingWebhookConfiguration.yaml
+++ b/charts/vela-core/templates/admission-webhooks/mutatingWebhookConfiguration.yaml
@@ -1,4 +1,14 @@
 {{- if .Values.admissionWebhooks.enabled -}}
+{{- /* Preserve existing caBundle on upgrade to avoid breaking admission if hooks fail. */}}
+{{- $mName := printf "%s-admission" (include "kubevela.fullname" .) -}}
+{{- $existing := (lookup "admissionregistration.k8s.io/v1" "MutatingWebhookConfiguration" "" $mName) -}}
+{{- $vals := dict "apps" "" "comps" "" -}}
+{{- if $existing -}}
+{{- range $existing.webhooks -}}
+{{- if eq .name "mutating.core.oam.dev.v1beta1.applications" -}}{{- $_ := set $vals "apps" .clientConfig.caBundle -}}{{- end -}}
+{{- if eq .name "mutating.core.oam-dev.v1beta1.componentdefinitions" -}}{{- $_ := set $vals "comps" .clientConfig.caBundle -}}{{- end -}}
+{{- end -}}
+{{- end -}}
 apiVersion: admissionregistration.k8s.io/v1
 kind: MutatingWebhookConfiguration
 metadata:
@@ -10,7 +20,7 @@ metadata:
   {{- end }}
 webhooks:
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "apps") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
@@ -36,7 +46,7 @@ webhooks:
         resources:
           - applications
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "comps") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
diff --git a/charts/vela-core/templates/admission-webhooks/validatingWebhookConfiguration.yaml b/charts/vela-core/templates/admission-webhooks/validatingWebhookConfiguration.yaml
index 270aed33..11ad4bbe 100644
--- a/charts/vela-core/templates/admission-webhooks/validatingWebhookConfiguration.yaml
+++ b/charts/vela-core/templates/admission-webhooks/validatingWebhookConfiguration.yaml
@@ -1,4 +1,16 @@
 {{- if .Values.admissionWebhooks.enabled -}}
+{{- /* Preserve existing caBundle on upgrade to avoid breaking admission if hooks fail. */}}
+{{- $vName := printf "%s-admission" (include "kubevela.fullname" .) -}}
+{{- $existing := (lookup "admissionregistration.k8s.io/v1" "ValidatingWebhookConfiguration" "" $vName) -}}
+{{- $vals := dict "traits" "" "apps" "" "comps" "" "policies" "" -}}
+{{- if $existing -}}
+{{- range $existing.webhooks -}}
+{{- if eq .name "validating.core.oam.dev.v1beta1.traitdefinitions" -}}{{- $_ := set $vals "traits" .clientConfig.caBundle -}}{{- end -}}
+{{- if eq .name "validating.core.oam.dev.v1beta1.applications" -}}{{- $_ := set $vals "apps" .clientConfig.caBundle -}}{{- end -}}
+{{- if eq .name "validating.core.oam-dev.v1beta1.componentdefinitions" -}}{{- $_ := set $vals "comps" .clientConfig.caBundle -}}{{- end -}}
+{{- if eq .name "validating.core.oam-dev.v1beta1.policydefinitions" -}}{{- $_ := set $vals "policies" .clientConfig.caBundle -}}{{- end -}}
+{{- end -}}
+{{- end -}}
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
 metadata:
@@ -10,7 +22,7 @@ metadata:
   {{- end }}
 webhooks:
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "traits") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
@@ -37,7 +49,7 @@ webhooks:
           - traitdefinitions
     timeoutSeconds: 5
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "apps") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
@@ -63,7 +75,7 @@ webhooks:
         resources:
           - applications
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "comps") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
@@ -89,7 +101,7 @@ webhooks:
         resources:
           - componentdefinitions
   - clientConfig:
-      caBundle: Cg==
+      caBundle: {{ default "Cg==" (get $vals "policies") }}
       service:
         name: {{ template "kubevela.name" . }}-webhook
         namespace: {{ .Release.Namespace }}
@@ -114,4 +126,30 @@ webhooks:
           - UPDATE
         resources:
           - policydefinitions
+  - clientConfig:
+      caBundle: Cg==
+      service:
+        name: {{ template "kubevela.name" . }}-webhook
+        namespace: {{ .Release.Namespace }}
+        path: /validating-core-oam-dev-v1beta1-workflowstepdefinitions
+    {{- if .Values.admissionWebhooks.patch.enabled  }}
+    failurePolicy: Ignore
+    {{- else }}
+    failurePolicy: Fail
+    {{- end }}
+    name: validating.core.oam-dev.v1beta1.workflowstepdefinitions
+    sideEffects: None
+    admissionReviewVersions:
+      - v1beta1
+      - v1
+    rules:
+      - apiGroups:
+          - core.oam.dev
+        apiVersions:
+          - v1beta1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - workflowstepdefinitions
 {{- end -}}
diff --git a/charts/vela-core/templates/defwithtemplate/vela-cli.yaml b/charts/vela-core/templates/defwithtemplate/vela-cli.yaml
index a1f349ca..e69ecacc 100644
--- a/charts/vela-core/templates/defwithtemplate/vela-cli.yaml
+++ b/charts/vela-core/templates/defwithtemplate/vela-cli.yaml
@@ -126,8 +126,6 @@ spec:
         }
 
         parameter: {
-        	// +usage=Specify the name of the addon.
-        	addonName: string
         	// +usage=Specify the vela command
         	command: [...string]
         	// +usage=Specify the image
diff --git a/charts/vela-core/templates/kubevela-controller.yaml b/charts/vela-core/templates/kubevela-controller.yaml
index 80bf8028..746ba142 100644
--- a/charts/vela-core/templates/kubevela-controller.yaml
+++ b/charts/vela-core/templates/kubevela-controller.yaml
@@ -313,8 +313,9 @@ spec:
             - "--feature-gates=DisableWorkflowContextConfigMapCache={{- .Values.featureGates.disableWorkflowContextConfigMapCache | toString -}}"
             - "--feature-gates=EnableCueValidation={{- .Values.featureGates.enableCueValidation | toString -}}"
             - "--feature-gates=EnableApplicationStatusMetrics={{- .Values.featureGates.enableApplicationStatusMetrics | toString -}}"
+            - "--feature-gates=ValidateResourcesExist={{- .Values.featureGates.validateResourcesExist | toString -}}"
+            - "--feature-gates=ValidateDefinitionPermissions={{ .Values.authorization.definitionValidationEnabled | toString -}}"
             {{ if .Values.authentication.enabled }}
-            - "--feature-gates=ValidateDefinitionPermissions={{ .Values.authentication.definitions.enabled | toString -}}"
             {{ if .Values.authentication.withUser }}
             - "--authentication-with-user"
             {{ end }}
@@ -327,6 +328,7 @@ spec:
             - "--feature-gates=ValidateComponentWhenSharding={{- .Values.featureGates.validateComponentWhenSharding | toString -}}"
             - "--feature-gates=DisableWebhookAutoSchedule={{- .Values.featureGates.disableWebhookAutoSchedule | toString -}}"
             {{ end }}
+            - "--dev-logs={{ .Values.devLogs }}"
           image: {{ .Values.imageRegistry }}{{ .Values.image.repository }}:{{ .Values.image.tag }}
           imagePullPolicy: {{ quote .Values.image.pullPolicy }}
           resources:
diff --git a/charts/vela-core/values.yaml b/charts/vela-core/values.yaml
index 2fa9592e..8b2d5fdb 100644
--- a/charts/vela-core/values.yaml
+++ b/charts/vela-core/values.yaml
@@ -125,6 +125,7 @@ optimize:
 ##@param featureGates.disableWorkflowContextConfigMapCache disable the workflow context's configmap informer cache
 ##@param featureGates.enableCueValidation enable the strict cue validation for cue required parameter fields
 ##@param featureGates.enableApplicationStatusMetrics enable application status metrics and structured logging
+##@param featureGates.validateResourcesExist enable webhook validation to check if resource types referenced in definition templates exist in the cluster
 ##@param
 featureGates:
   gzipResourceTracker: false
@@ -142,6 +143,7 @@ featureGates:
   disableWorkflowContextConfigMapCache: true
   enableCueValidation: false
   enableApplicationStatusMetrics: false
+  validateResourcesExist: false
 
 ## @section MultiCluster parameters
 
@@ -256,6 +258,9 @@ rbac:
 ## @param logDebug Enable debug logs for development purpose
 logDebug: false
 
+## @param devLogs Enable formatted logging support for development purpose
+devLogs: false
+
 ## @param logFilePath If non-empty, write log files in this path
 logFilePath: ""
 
@@ -290,18 +295,27 @@ kubeClient:
   burst: 600
 
 ## @param authentication.enabled Enable authentication framework for applications
-## @param authentication.definitions.enabled Enable definition permission validation (requires authentication.enabled)  
-## @param authentication.withUser Application authentication will impersonate as the request User
-## @param authentication.defaultUser Application authentication will impersonate as the User if no user provided in Application
+## SECURITY NOTE: When enabled WITH authentication.withUser=true, KubeVela impersonates the requesting user
+## when deploying resources, ensuring that users cannot deploy resources beyond their RBAC permissions.
+## This is strongly recommended when KubeVela has cluster-admin or other high-level permissions.
+## @param authentication.withUser Application authentication will impersonate as the request User (must be true for security)
+## @param authentication.defaultUser Application authentication will impersonate as the User if no user provided or withUser is false
 ## @param authentication.groupPattern Application authentication will impersonate as the request Group that matches the pattern
 authentication:
   enabled: false
-  definitions:
-    enabled: false
   withUser: true
   defaultUser: kubevela:vela-core
   groupPattern: kubevela:*
 
+## @param authorization.definitionValidationEnabled Enable definition permission validation for RBAC checks on definitions
+## SECURITY NOTE: If KubeVela is running with cluster-admin or high-level permissions,
+## consider enabling this feature and/or authentication.enabled for security:
+## - This feature: Validates users can only reference definitions they have RBAC access to
+## - authentication.enabled: Makes KubeVela impersonate users, applying their full RBAC permissions
+## Both features can be used together for defense in depth.
+authorization:
+  definitionValidationEnabled: false
+
 ## @param sharding.enabled When sharding enabled, the controller will run as master mode. Refer to https://github.com/kubevela/kubevela/blob/master/design/vela-core/sharding.md for details.
 ## @param sharding.schedulableShards The shards available for scheduling. If empty, dynamic discovery will be used.
 sharding:
diff --git a/cmd/core/app/color_writer.go b/cmd/core/app/color_writer.go
new file mode 100644
index 00000000..dd1fc51a
--- /dev/null
+++ b/cmd/core/app/color_writer.go
@@ -0,0 +1,580 @@
+/*
+Copyright 2022 The KubeVela 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 app
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"unicode"
+)
+
+const (
+	ansiReset    = "\x1b[0m"
+	ansiDate     = "\x1b[36m"
+	ansiInfo     = "\x1b[32m"
+	ansiWarn     = "\x1b[33m"
+	ansiError    = "\x1b[31m"
+	ansiFatal    = "\x1b[35m"
+	ansiDebug    = "\x1b[34m"
+	ansiThread   = "\x1b[34m"
+	ansiLocation = "\x1b[93m"
+	ansiMessage  = "\x1b[97m"
+	ansiKey      = "\x1b[96m"
+	ansiValue    = "\x1b[37m"
+)
+
+var methodPatterns = []*regexp.Regexp{
+	regexp.MustCompile(`(?i)\b(?:caller|func|function|method)\s*[:=]\s*"?([a-zA-Z0-9_./()*-]+)`),
+}
+
+var srcIdx = newSourceIndex()
+
+type colorWriter struct {
+	dst io.Writer
+	mu  sync.Mutex
+	buf bytes.Buffer
+}
+
+func newColorWriter(dst io.Writer) io.Writer {
+	return &colorWriter{dst: dst}
+}
+
+func (w *colorWriter) Write(p []byte) (int, error) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	written := 0
+	for len(p) > 0 {
+		idx := bytes.IndexByte(p, '\n')
+		if idx == -1 {
+			_, _ = w.buf.Write(p)
+			written += len(p)
+			break
+		}
+		_, _ = w.buf.Write(p[:idx])
+		if err := w.flushLineLocked(); err != nil {
+			written += idx
+			return written, err
+		}
+		if _, err := w.dst.Write([]byte{'\n'}); err != nil {
+			written += idx + 1
+			return written, err
+		}
+		p = p[idx+1:]
+		written += idx + 1
+	}
+	return written, nil
+}
+
+func (w *colorWriter) flushLineLocked() error {
+	if w.buf.Len() == 0 {
+		return nil
+	}
+	line := w.buf.String()
+	w.buf.Reset()
+	_, err := io.WriteString(w.dst, formatColoredLine(line))
+	return err
+}
+
+func formatColoredLine(line string) string {
+	if len(line) == 0 {
+		return line
+	}
+	severity := line[0]
+	rest := line[1:]
+	headerEnd := strings.IndexByte(rest, ']')
+	if headerEnd == -1 {
+		return line
+	}
+	header := rest[:headerEnd]
+	remainder := strings.TrimSpace(rest[headerEnd+1:])
+	fields := strings.Fields(header)
+	if len(fields) < 4 {
+		return line
+	}
+
+	date := fields[0]
+	ts := fields[1]
+	pid := strings.TrimSpace(fields[2])
+	rawLocation := strings.Join(fields[3:], " ")
+	level, levelColor := mapSeverity(severity)
+	location := buildLocation(rawLocation, remainder)
+
+	sb := strings.Builder{}
+	sb.Grow(len(line) + 32)
+
+	sb.WriteString(ansiDate)
+	sb.WriteString("[")
+	sb.WriteString(formatTimestamp(date, ts))
+	sb.WriteString("]")
+	sb.WriteString(ansiReset)
+	sb.WriteString(" ")
+
+	sb.WriteString(levelColor)
+	sb.WriteString("[")
+	sb.WriteString(fmt.Sprintf("%-5s", level))
+	sb.WriteString("]")
+	sb.WriteString(ansiReset)
+	sb.WriteString(" ")
+
+	sb.WriteString(ansiThread)
+	sb.WriteString("[")
+	sb.WriteString(pid)
+	sb.WriteString("]")
+	sb.WriteString(ansiReset)
+	sb.WriteString(" ")
+
+	sb.WriteString(ansiLocation)
+	sb.WriteString("[")
+	sb.WriteString(location)
+	sb.WriteString("]")
+	sb.WriteString(ansiReset)
+
+	if remainder != "" {
+		msg, fields := splitMessageAndFields(remainder)
+		if msg != "" {
+			sb.WriteString(" ")
+			sb.WriteString(ansiMessage)
+			sb.WriteString(msg)
+			sb.WriteString(ansiReset)
+		}
+		for _, field := range fields {
+			sb.WriteString(" ")
+			sb.WriteString(ansiKey)
+			sb.WriteString(field.key)
+			sb.WriteString(ansiReset)
+			sb.WriteString("=")
+			sb.WriteString(ansiValue)
+			sb.WriteString(field.renderValue())
+			sb.WriteString(ansiReset)
+		}
+	}
+
+	return sb.String()
+}
+
+func mapSeverity(s byte) (string, string) {
+	switch s {
+	case 'I':
+		return "INFO", ansiInfo
+	case 'W':
+		return "WARN", ansiWarn
+	case 'E':
+		return "ERROR", ansiError
+	case 'F':
+		return "FATAL", ansiFatal
+	case 'D':
+		return "DEBUG", ansiDebug
+	default:
+		return string(s), ansiDebug
+	}
+}
+
+func formatTimestamp(date, ts string) string {
+	if len(date) == 4 {
+		return fmt.Sprintf("%s-%s %s", date[:2], date[2:], trimMicros(ts))
+	}
+	return strings.TrimSpace(date + " " + trimMicros(ts))
+}
+
+func trimMicros(ts string) string {
+	if dot := strings.IndexByte(ts, '.'); dot != -1 && len(ts) > dot+4 {
+		return ts[:dot+4]
+	}
+	return ts
+}
+
+func buildLocation(rawLocation, remainder string) string {
+	file, line := splitFileAndLine(rawLocation)
+	method := lookupMethod(file, line)
+	if method == "" {
+		method = extractMethodName(remainder)
+	}
+	if file == "" {
+		return rawLocation
+	}
+	if method == "" {
+		if line != "" {
+			return fmt.Sprintf("%s:%s", file, line)
+		}
+		return file
+	}
+	if line == "" {
+		return fmt.Sprintf("%s:%s", file, method)
+	}
+	return fmt.Sprintf("%s:%s:%s", file, method, line)
+}
+
+func splitFileAndLine(raw string) (string, string) {
+	trimmed := strings.TrimSpace(raw)
+	if trimmed == "" {
+		return "", ""
+	}
+	lastColon := strings.LastIndex(trimmed, ":")
+	if lastColon == -1 {
+		return trimmed, ""
+	}
+	return strings.TrimSpace(trimmed[:lastColon]), strings.TrimSpace(trimmed[lastColon+1:])
+}
+
+type kvField struct {
+	key    string
+	value  string
+	quoted bool
+}
+
+func (f kvField) renderValue() string {
+	if f.quoted {
+		return fmt.Sprintf("\"%s\"", f.value)
+	}
+	return f.value
+}
+
+func splitMessageAndFields(s string) (string, []kvField) {
+	s = strings.TrimSpace(s)
+	if s == "" {
+		return "", nil
+	}
+	idx := findFirstKVIndex(s)
+	if idx == -1 {
+		return s, nil
+	}
+	msg := strings.TrimSpace(s[:idx])
+	fields := parseKeyValues(s[idx:])
+	return msg, fields
+}
+
+func findFirstKVIndex(s string) int {
+	inQuotes := false
+	for i := 0; i < len(s); i++ {
+		ch := s[i]
+		if ch == '"' {
+			if i == 0 || s[i-1] != '\\' {
+				inQuotes = !inQuotes
+			}
+			continue
+		}
+		if inQuotes {
+			continue
+		}
+		if ch == '=' {
+			keyEnd := i
+			keyStart := keyEnd - 1
+			for keyStart >= 0 && !unicode.IsSpace(rune(s[keyStart])) {
+				keyStart--
+			}
+			key := strings.TrimSpace(s[keyStart+1 : keyEnd])
+			if isValidKey(key) {
+				return keyStart + 1
+			}
+		}
+	}
+	return -1
+}
+
+func parseKeyValues(s string) []kvField {
+	fields := make([]kvField, 0)
+	i := 0
+	for i < len(s) {
+		for i < len(s) && unicode.IsSpace(rune(s[i])) {
+			i++
+		}
+		if i >= len(s) {
+			break
+		}
+		keyStart := i
+		for i < len(s) && s[i] != '=' && !unicode.IsSpace(rune(s[i])) {
+			i++
+		}
+		keyEnd := i
+		for keyEnd > keyStart && unicode.IsSpace(rune(s[keyEnd-1])) {
+			keyEnd--
+		}
+		if i >= len(s) || s[i] != '=' {
+			for i < len(s) && !unicode.IsSpace(rune(s[i])) {
+				i++
+			}
+			continue
+		}
+		key := s[keyStart:keyEnd]
+		if !isValidKey(key) {
+			i++
+			continue
+		}
+		i++
+		for i < len(s) && unicode.IsSpace(rune(s[i])) {
+			i++
+		}
+		if i >= len(s) {
+			fields = append(fields, kvField{key: key, value: "", quoted: false})
+			break
+		}
+		quoted := false
+		var value string
+		if s[i] == '"' {
+			quoted = true
+			i++
+			start := i
+			for i < len(s) {
+				if s[i] == '"' && (i == start || s[i-1] != '\\') {
+					break
+				}
+				i++
+			}
+			value = s[start:i]
+			if i < len(s) && s[i] == '"' {
+				i++
+			}
+		} else {
+			start := i
+			for i < len(s) && !unicode.IsSpace(rune(s[i])) {
+				i++
+			}
+			value = s[start:i]
+		}
+		fields = append(fields, kvField{key: key, value: value, quoted: quoted})
+	}
+	return fields
+}
+
+func isValidKey(key string) bool {
+	if key == "" {
+		return false
+	}
+	for _, r := range key {
+		if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '-' || r == '.' {
+			continue
+		}
+		return false
+	}
+	return true
+}
+
+func extractMethodName(remainder string) string {
+	if remainder == "" {
+		return ""
+	}
+	for _, pattern := range methodPatterns {
+		if matches := pattern.FindStringSubmatch(remainder); len(matches) > 1 {
+			return simplifyMethodName(matches[1])
+		}
+	}
+	return ""
+}
+
+func simplifyMethodName(name string) string {
+	if name == "" {
+		return ""
+	}
+	name = strings.Trim(name, "\"' ")
+	if idx := strings.LastIndex(name, "/"); idx != -1 {
+		name = name[idx+1:]
+	}
+	return name
+}
+
+func lookupMethod(file, line string) string {
+	if file == "" || line == "" {
+		return ""
+	}
+	ln, err := strconv.Atoi(line)
+	if err != nil || ln <= 0 {
+		return ""
+	}
+	return srcIdx.functionAt(file, ln)
+}
+
+type funcInfo struct {
+	name      string
+	startLine int
+	endLine   int
+}
+
+type fileCache struct {
+	once  sync.Once
+	funcs []funcInfo
+}
+
+type sourceIndex struct {
+	mu    sync.Mutex
+	files map[string]*fileCache
+}
+
+func newSourceIndex() *sourceIndex {
+	return &sourceIndex{files: make(map[string]*fileCache)}
+}
+
+func (s *sourceIndex) functionAt(base string, line int) string {
+	s.mu.Lock()
+	cache, ok := s.files[base]
+	if !ok {
+		cache = &fileCache{}
+		s.files[base] = cache
+	}
+	s.mu.Unlock()
+
+	cache.once.Do(func() {
+		cache.funcs = loadFunctionsFor(base)
+	})
+	for _, fn := range cache.funcs {
+		if line >= fn.startLine && line <= fn.endLine {
+			return fn.name
+		}
+	}
+	return ""
+}
+
+func loadFunctionsFor(base string) []funcInfo {
+	paths := findSourceFiles(base)
+	if len(paths) == 0 {
+		return nil
+	}
+	infos := make([]funcInfo, 0, len(paths)*4)
+	for _, path := range paths {
+		fset := token.NewFileSet()
+		fileAst, err := parser.ParseFile(fset, path, nil, 0)
+		if err != nil {
+			continue
+		}
+		for _, decl := range fileAst.Decls {
+			fn, ok := decl.(*ast.FuncDecl)
+			if !ok || fn.Name == nil {
+				continue
+			}
+			start := fset.Position(fn.Pos()).Line
+			end := fset.Position(fn.End()).Line
+			name := fn.Name.Name
+			if recv := receiverType(fn); recv != "" {
+				name = fmt.Sprintf("%s.%s", recv, name)
+			}
+			infos = append(infos, funcInfo{name: name, startLine: start, endLine: end})
+		}
+	}
+	return infos
+}
+
+func receiverType(fn *ast.FuncDecl) string {
+	if fn.Recv == nil || len(fn.Recv.List) == 0 {
+		return ""
+	}
+	return typeString(fn.Recv.List[0].Type)
+}
+
+func typeString(expr ast.Expr) string {
+	switch v := expr.(type) {
+	case *ast.Ident:
+		return v.Name
+	case *ast.StarExpr:
+		return typeString(v.X)
+	case *ast.SelectorExpr:
+		return v.Sel.Name
+	case *ast.IndexExpr:
+		return typeString(v.X)
+	case *ast.IndexListExpr:
+		return typeString(v.X)
+	case *ast.ParenExpr:
+		return typeString(v.X)
+	default:
+		return ""
+	}
+}
+
+var (
+	searchRootsOnce sync.Once
+	cachedRoots     []string
+)
+
+func findSourceFiles(base string) []string {
+	matches := make([]string, 0)
+	seen := make(map[string]struct{})
+	for _, root := range sourceRoots() {
+		if root == "" {
+			continue
+		}
+		lookupFilesInRoot(root, base, seen, &matches)
+	}
+	return matches
+}
+
+func sourceRoots() []string {
+	searchRootsOnce.Do(func() {
+		roots := make([]string, 0, 4)
+		if env := os.Getenv("VELA_SOURCE_ROOT"); env != "" {
+			for _, item := range strings.Split(env, string(os.PathListSeparator)) {
+				if trimmed := strings.TrimSpace(item); trimmed != "" {
+					roots = append(roots, trimmed)
+				}
+			}
+		}
+		if cwd, err := os.Getwd(); err == nil {
+			roots = append(roots, cwd)
+		}
+		cachedRoots = dedupeStrings(roots)
+	})
+	return cachedRoots
+}
+
+func lookupFilesInRoot(root, base string, seen map[string]struct{}, matches *[]string) {
+	_ = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			// Continue scanning on error.
+			//nolint:nilerr
+			return nil
+		}
+		if d.IsDir() {
+			name := d.Name()
+			if name == "vendor" || name == "node_modules" || strings.HasPrefix(name, ".") {
+				if root == path {
+					return nil
+				}
+				return filepath.SkipDir
+			}
+			return nil
+		}
+		if d.Name() == base {
+			if _, ok := seen[path]; !ok {
+				seen[path] = struct{}{}
+				*matches = append(*matches, path)
+			}
+		}
+		return nil
+	})
+}
+
+func dedupeStrings(in []string) []string {
+	seen := make(map[string]struct{}, len(in))
+	out := make([]string, 0, len(in))
+	for _, v := range in {
+		if _, ok := seen[v]; ok {
+			continue
+		}
+		seen[v] = struct{}{}
+		out = append(out, v)
+	}
+	return out
+}
diff --git a/cmd/core/app/options/options.go b/cmd/core/app/options/options.go
index be3096d3..7959c0f8 100644
--- a/cmd/core/app/options/options.go
+++ b/cmd/core/app/options/options.go
@@ -50,6 +50,7 @@ type CoreOptions struct {
 	LogFilePath             string
 	LogFileMaxSize          uint64
 	LogDebug                bool
+	DevLogs                 bool
 	ControllerArgs          *oamcontroller.Args
 	HealthAddr              string
 	StorageDriver           string
@@ -76,6 +77,7 @@ func NewCoreOptions() *CoreOptions {
 		LogFilePath:             "",
 		LogFileMaxSize:          1024,
 		LogDebug:                false,
+		DevLogs:                 false,
 		ControllerArgs: &oamcontroller.Args{
 			RevisionLimit:                                50,
 			AppRevisionLimit:                             10,
@@ -116,6 +118,7 @@ func (s *CoreOptions) Flags() cliflag.NamedFlagSets {
 	gfs.StringVar(&s.LogFilePath, "log-file-path", s.LogFilePath, "The file to write logs to.")
 	gfs.Uint64Var(&s.LogFileMaxSize, "log-file-max-size", s.LogFileMaxSize, "Defines the maximum size a log file can grow to, Unit is megabytes.")
 	gfs.BoolVar(&s.LogDebug, "log-debug", s.LogDebug, "Enable debug logs for development purpose")
+	gfs.BoolVar(&s.DevLogs, "dev-logs", s.DevLogs, "Enable ANSI color formatting for console logs (ignored when log-file-path is set)")
 	gfs.StringVar(&s.HealthAddr, "health-addr", s.HealthAddr, "The address the health endpoint binds to.")
 	gfs.DurationVar(&s.InformerSyncPeriod, "informer-sync-period", s.InformerSyncPeriod,
 		"The re-sync period for informer in controller-runtime. This is a system-level configuration.")
diff --git a/cmd/core/app/server.go b/cmd/core/app/server.go
index 06482191..36eef859 100644
--- a/cmd/core/app/server.go
+++ b/cmd/core/app/server.go
@@ -103,6 +103,17 @@ func run(ctx context.Context, s *options.CoreOptions) error {
 	restConfig.QPS = float32(s.QPS)
 	restConfig.Burst = s.Burst
 	restConfig.Wrap(auth.NewImpersonatingRoundTripper)
+
+	// Set logger (use --dev-logs=true for local development)
+	if s.DevLogs {
+		logOutput := newColorWriter(os.Stdout)
+		klog.LogToStderr(false)
+		klog.SetOutput(logOutput)
+		ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig(textlogger.Output(logOutput))))
+	} else {
+		ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig()))
+	}
+
 	klog.InfoS("Kubernetes Config Loaded",
 		"UserAgent", restConfig.UserAgent,
 		"QPS", restConfig.QPS,
@@ -127,8 +138,6 @@ func run(ctx context.Context, s *options.CoreOptions) error {
 		}
 	}
 
-	ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig()))
-
 	if utilfeature.DefaultMutableFeatureGate.Enabled(features.ApplyOnce) {
 		commonconfig.ApplicationReSyncPeriod = s.InformerSyncPeriod
 	}
diff --git a/docs/WEBHOOK_DEBUGGING.md b/docs/WEBHOOK_DEBUGGING.md
new file mode 100644
index 00000000..ad544c38
--- /dev/null
+++ b/docs/WEBHOOK_DEBUGGING.md
@@ -0,0 +1,191 @@
+# KubeVela Webhook Debugging Guide
+
+This guide explains how to debug KubeVela webhook validation locally, particularly for the feature that validates ComponentDefinitions, TraitDefinitions, and PolicyDefinitions to ensure they don't reference non-existent CRDs.
+
+## Overview
+
+The webhook validation feature checks that CUE templates in definitions only reference Kubernetes resources that exist on the cluster. This prevents runtime errors when non-existent CRDs are referenced.
+
+## Prerequisites
+
+- Docker Desktop or similar container runtime
+- k3d for local Kubernetes clusters
+- VS Code with Go extension
+- kubectl configured
+- openssl for certificate generation
+
+## Quick Start
+
+```bash
+# 1. Complete setup (cluster + CRDs + webhook)
+make webhook-debug-setup
+
+# 2. Start VS Code debugger
+# Press F5 and select "Debug Webhook Validation"
+```
+
+## Detailed Setup Steps
+
+### 1. Environment Setup
+
+```bash
+# Create k3d cluster
+make k3d-create
+
+# Install KubeVela CRDs
+make manifests
+kubectl apply -f charts/vela-core/crds/
+```
+
+### 2. Webhook Certificate Setup
+
+The webhook requires TLS certificates with proper Subject Alternative Names (SANs) for IP addresses.
+
+```bash
+# Generate certificates and create Kubernetes secret
+make webhook-setup
+```
+
+This creates:
+- CA certificate and key
+- Server certificate with IP SANs (127.0.0.1, Docker internal IP, local machine IP)
+- Kubernetes Secret `webhook-server-cert` in `vela-system` namespace
+- ValidatingWebhookConfiguration pointing to local debugger
+
+### 3. Start Debugger in VS Code
+
+#### VS Code Launch Configuration
+
+If you're using VSCode add this configuration to `.vscode/launch.json`:
+
+```json
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Debug Webhook Validation",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceFolder}/cmd/core",
+            "args": [
+                "--log-debug=true",
+                "--metrics-addr=:8080",
+                "--enable-leader-election=false",
+                "--use-webhook=true",
+                "--webhook-port=9445",
+                "--webhook-cert-dir=${workspaceFolder}/k8s-webhook-server/serving-certs"
+            ],
+            "env": {
+                "KUBECONFIG": "${env:HOME}/.kube/config",
+                "POD_NAMESPACE": "vela-system"
+            },
+            "showLog": false,
+            "console": "integratedTerminal"
+        }
+    ]
+}
+```
+
+#### Set Breakpoints
+
+Recommended breakpoint locations:
+- `pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler.go` - ComponentDefinition handler
+- `pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler.go` - TraitDefinition handler
+- `pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler.go` - PolicyDefinition handler
+- `pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler.go` - WorkflowDefinition handler
+
+#### Launch Debugger
+
+1. Open VS Code
+2. Press `F5` or go to Run → Start Debugging
+3. Select **"Debug Webhook Validation"** configuration
+4. Wait for webhook server to start (look for message about port 9445)
+
+The debugger configuration includes:
+- `--use-webhook=true` - Enables webhook server
+- `--webhook-port=9445` - Port for webhook server
+- `--webhook-cert-dir` - Path to certificates
+- `POD_NAMESPACE=vela-system` - Required for finding the secret
+
+## Make Targets Reference
+
+| Target | Description |
+|--------|-------------|
+| `make webhook-help` | Show webhook debugging help |
+| `make webhook-debug-setup` | Complete setup (cluster + CRDs + webhook) |
+| `make k3d-create` | Create k3d cluster |
+| `make k3d-delete` | Delete k3d cluster |
+| `make webhook-setup` | Setup certificates and webhook configuration |
+| `make webhook-clean` | Clean up webhook environment |
+
+## Troubleshooting
+
+### Connection Refused Error
+
+If you get "connection refused" errors:
+1. Ensure the debugger is running in VS Code
+2. Check that port 9445 is not blocked by firewall
+3. Verify the webhook server started (check VS Code console)
+
+### TLS Certificate Errors
+
+If you get certificate validation errors:
+1. Regenerate certificates: `make webhook-setup`
+2. Restart the debugger
+3. Ensure IP addresses in certificates match your setup
+
+### Webhook Not Triggering
+
+If the webhook doesn't trigger:
+1. Check ValidatingWebhookConfiguration: `kubectl get validatingwebhookconfiguration`
+2. Verify the webhook URL matches your debugger's IP
+3. Check namespace is correct (vela-system)
+
+### Secret Not Found
+
+If you see "Wait webhook secret" messages:
+1. Ensure the secret exists: `kubectl get secret webhook-server-cert -n vela-system`
+2. Recreate if needed: `make webhook-setup`
+
+## How It Works
+
+1. **Certificate Generation**: Creates TLS certificates with proper SANs for local IPs
+2. **Secret Creation**: Stores certificates in Kubernetes secret
+3. **Webhook Configuration**: Creates ValidatingWebhookConfiguration pointing to local debugger
+4. **Debugger Startup**: VS Code starts the webhook server on port 9445
+5. **Validation**: When definitions are applied, Kubernetes calls the webhook
+6. **Debugging**: Breakpoints allow stepping through validation logic
+
+## Files and Components
+
+- **Script**: `hack/debug-webhook-setup.sh` - Main setup script
+- **Makefile**: `makefiles/develop.mk` - Make targets for debugging
+- **VS Code Config**: `.vscode/launch.json` - Debugger configuration
+- **Test Files**: `test/webhook-*.yaml` - Test manifests
+- **Validation Logic**: `pkg/webhook/utils/utils.go` - Core validation implementation
+- **Handlers**: `pkg/webhook/core.oam.dev/v1beta1/*/validating_handler.go` - Resource handlers
+
+## Clean Up
+
+```bash
+# Clean up webhook setup
+make webhook-clean
+
+# Delete k3d cluster
+make k3d-delete
+```
+
+## Tips
+
+1. **Always start the debugger before testing** - The webhook configuration points to your local machine
+2. **Use breakpoints wisely** - Too many breakpoints can cause timeouts
+3. **Check logs** - VS Code Debug Console shows detailed logs
+4. **Test both valid and invalid cases** - Ensures validation works correctly
+5. **Keep certificates updated** - Regenerate if IPs change
+
+## Related Documentation
+
+- [KubeVela Webhook Implementation](../pkg/webhook/README.md)
+- [CUE Template Validation](../pkg/webhook/utils/README.md)
+- [Admission Webhooks](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
\ No newline at end of file
diff --git a/e2e/addon/mock/testrepo/helm-repo/index.yaml b/e2e/addon/mock/testrepo/helm-repo/index.yaml
index fc089d7d..87ee524c 100644
--- a/e2e/addon/mock/testrepo/helm-repo/index.yaml
+++ b/e2e/addon/mock/testrepo/helm-repo/index.yaml
@@ -32,8 +32,8 @@ entries:
       icon: https://static.kubevela.net/images/logos/KubeVela%20-03.png
       name: vela-workflow
       urls:
-        - http://127.0.0.1:9098/helm/vela-workflow-v0.3.5.tgz
-      version: v0.3.5
+        - http://127.0.0.1:9098/helm/vela-workflow-v0.6.2.tgz
+      version: v0.6.2
   foo:
     - created: "2022-10-29T09:11:16.865230605Z"
       description: Vela test addon named foo
diff --git a/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.5.tgz b/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.5.tgz
deleted file mode 100644
index 0850ee9e..00000000
Binary files a/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.5.tgz and /dev/null differ
diff --git a/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.6.2.tgz b/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.6.2.tgz
new file mode 100644
index 00000000..a017b014
Binary files /dev/null and b/e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.6.2.tgz differ
diff --git a/e2e/addon/mock/vela_addon_mock_server.go b/e2e/addon/mock/vela_addon_mock_server.go
index 31cb8098..279d99e0 100644
--- a/e2e/addon/mock/vela_addon_mock_server.go
+++ b/e2e/addon/mock/vela_addon_mock_server.go
@@ -125,8 +125,8 @@ var helmHandler http.HandlerFunc = func(rw http.ResponseWriter, req *http.Reques
 			_, _ = rw.Write([]byte(err.Error()))
 		}
 		rw.Write(file)
-	case strings.Contains(req.URL.Path, "vela-workflow-v0.3.5.tgz"):
-		file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.5.tgz")
+	case strings.Contains(req.URL.Path, "vela-workflow-v0.6.2.tgz"):
+		file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.6.2.tgz")
 		if err != nil {
 			_, _ = rw.Write([]byte(err.Error()))
 		}
diff --git a/hack/debug-webhook-setup.sh b/hack/debug-webhook-setup.sh
new file mode 100755
index 00000000..54b069ac
--- /dev/null
+++ b/hack/debug-webhook-setup.sh
@@ -0,0 +1,243 @@
+#!/bin/bash
+# Webhook debugging setup script for KubeVela
+# This script sets up everything needed to debug webhooks locally
+
+set -e
+
+# Configuration
+CERT_DIR="k8s-webhook-server/serving-certs"
+NAMESPACE="vela-system"
+SECRET_NAME="webhook-server-cert"
+WEBHOOK_CONFIG_NAME="kubevela-vela-core-admission"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo -e "${GREEN}=== KubeVela Webhook Debug Setup ===${NC}"
+
+# Function to check prerequisites
+check_prerequisites() {
+    echo -e "${YELLOW}Checking prerequisites...${NC}"
+
+    # Check kubectl
+    if ! command -v kubectl &> /dev/null; then
+        echo -e "${RED}kubectl is not installed${NC}"
+        exit 1
+    fi
+
+    # Check openssl
+    if ! command -v openssl &> /dev/null; then
+        echo -e "${RED}openssl is not installed${NC}"
+        exit 1
+    fi
+
+    # Check cluster connectivity
+    if ! kubectl cluster-info &> /dev/null; then
+        echo -e "${RED}Cannot connect to Kubernetes cluster${NC}"
+        echo "Please ensure your kubeconfig is set up correctly"
+        exit 1
+    fi
+
+    # Wait for cluster to be ready
+    echo "Waiting for cluster nodes to be ready..."
+    kubectl wait --for=condition=Ready nodes --all --timeout=60s &> /dev/null || true
+
+    echo -e "${GREEN}Prerequisites check passed${NC}"
+}
+
+# Function to create namespace if not exists
+create_namespace() {
+    echo -e "${YELLOW}Creating namespace ${NAMESPACE}...${NC}"
+    kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
+    echo -e "${GREEN}Namespace ready${NC}"
+}
+
+# Function to generate certificates
+generate_certificates() {
+    echo -e "${YELLOW}Generating webhook certificates...${NC}"
+
+    # Create directory
+    mkdir -p ${CERT_DIR}
+
+    # Clean old certificates
+    rm -f ${CERT_DIR}/*
+
+    # Generate CA private key
+    openssl genrsa -out ${CERT_DIR}/ca.key 2048
+
+    # Generate CA certificate
+    openssl req -x509 -new -nodes -key ${CERT_DIR}/ca.key -days 365 -out ${CERT_DIR}/ca.crt \
+        -subj "/CN=webhook-ca"
+
+    # Generate server private key
+    openssl genrsa -out ${CERT_DIR}/tls.key 2048
+
+    # Get host IP for Docker internal network
+    # NOTE: 192.168.5.2 is the standard k3d host gateway IP that allows containers to reach the host machine
+    # This is only for local k3d development environments - DO NOT use this script in production
+    # With failurePolicy: Fail, an unreachable webhook can block CRD operations cluster-wide
+    HOST_IP="192.168.5.2"
+    LOCAL_IP=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 | awk '{print $2}')
+
+    # Create certificate config with SANs
+    cat > /tmp/webhook.conf << EOF
+[req]
+req_extensions = v3_req
+distinguished_name = req_distinguished_name
+[req_distinguished_name]
+[v3_req]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+[alt_names]
+DNS.1 = localhost
+DNS.2 = vela-webhook.${NAMESPACE}.svc
+DNS.3 = vela-webhook.${NAMESPACE}.svc.cluster.local
+DNS.4 = *.${NAMESPACE}.svc
+DNS.5 = *.${NAMESPACE}.svc.cluster.local
+IP.1 = 127.0.0.1
+IP.2 = ${HOST_IP}
+IP.3 = ${LOCAL_IP}
+EOF
+
+    # Generate certificate request
+    openssl req -new -key ${CERT_DIR}/tls.key -out /tmp/server.csr \
+        -subj "/CN=vela-webhook.${NAMESPACE}.svc" -config /tmp/webhook.conf
+
+    # Generate server certificate with SANs
+    openssl x509 -req -in /tmp/server.csr -CA ${CERT_DIR}/ca.crt -CAkey ${CERT_DIR}/ca.key \
+        -CAcreateserial -out ${CERT_DIR}/tls.crt -days 365 \
+        -extensions v3_req -extfile /tmp/webhook.conf
+
+    echo -e "${GREEN}Certificates generated with IP SANs: 127.0.0.1, ${HOST_IP}, ${LOCAL_IP}${NC}"
+
+    # Clean up temp files
+    rm -f /tmp/server.csr /tmp/webhook.conf
+}
+
+# Function to create Kubernetes secret
+create_k8s_secret() {
+    echo -e "${YELLOW}Creating Kubernetes secret...${NC}"
+
+    # Delete old secret if exists
+    kubectl delete secret ${SECRET_NAME} -n ${NAMESPACE} --ignore-not-found
+
+    # Create new secret
+    kubectl create secret tls ${SECRET_NAME} \
+        --cert=${CERT_DIR}/tls.crt \
+        --key=${CERT_DIR}/tls.key \
+        -n ${NAMESPACE}
+
+    echo -e "${GREEN}Secret ${SECRET_NAME} created in namespace ${NAMESPACE}${NC}"
+}
+
+# Function to create webhook configuration
+create_webhook_config() {
+    echo -e "${YELLOW}Creating webhook configuration...${NC}"
+
+    # Get CA bundle
+    CA_BUNDLE=$(cat ${CERT_DIR}/ca.crt | base64 | tr -d '\n')
+
+    # Delete old webhook configuration if exists
+    kubectl delete validatingwebhookconfiguration ${WEBHOOK_CONFIG_NAME} --ignore-not-found
+
+    # Create webhook configuration
+    cat > /tmp/webhook-config.yaml << EOF
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+  name: ${WEBHOOK_CONFIG_NAME}
+webhooks:
+- name: componentdefinition.core.oam.dev
+  clientConfig:
+    url: https://${HOST_IP}:9445/validating-core-oam-dev-v1beta1-componentdefinitions
+    caBundle: ${CA_BUNDLE}
+  rules:
+  - apiGroups: ["core.oam.dev"]
+    apiVersions: ["v1beta1"]
+    resources: ["componentdefinitions"]
+    operations: ["CREATE", "UPDATE"]
+  admissionReviewVersions: ["v1", "v1beta1"]
+  sideEffects: None
+  failurePolicy: Fail
+- name: traitdefinition.core.oam.dev
+  clientConfig:
+    url: https://${HOST_IP}:9445/validating-core-oam-dev-v1beta1-traitdefinitions
+    caBundle: ${CA_BUNDLE}
+  rules:
+  - apiGroups: ["core.oam.dev"]
+    apiVersions: ["v1beta1"]
+    resources: ["traitdefinitions"]
+    operations: ["CREATE", "UPDATE"]
+  admissionReviewVersions: ["v1", "v1beta1"]
+  sideEffects: None
+  failurePolicy: Fail
+- name: policydefinition.core.oam.dev
+  clientConfig:
+    url: https://${HOST_IP}:9445/validating-core-oam-dev-v1beta1-policydefinitions
+    caBundle: ${CA_BUNDLE}
+  rules:
+  - apiGroups: ["core.oam.dev"]
+    apiVersions: ["v1beta1"]
+    resources: ["policydefinitions"]
+    operations: ["CREATE", "UPDATE"]
+  admissionReviewVersions: ["v1", "v1beta1"]
+  sideEffects: None
+  failurePolicy: Fail
+- name: workflowstepdefinition.core.oam.dev
+  clientConfig:
+    url: https://${HOST_IP}:9445/validating-core-oam-dev-v1beta1-workflowstepdefinitions
+    caBundle: ${CA_BUNDLE}
+  rules:
+  - apiGroups: ["core.oam.dev"]
+    apiVersions: ["v1beta1"]
+    resources: ["workflowstepdefinitions"]
+    operations: ["CREATE", "UPDATE"]
+  admissionReviewVersions: ["v1", "v1beta1"]
+  sideEffects: None
+  failurePolicy: Fail
+EOF
+
+    kubectl apply -f /tmp/webhook-config.yaml
+    rm -f /tmp/webhook-config.yaml
+
+    echo -e "${GREEN}Webhook configuration created${NC}"
+}
+
+# Function to show next steps
+show_next_steps() {
+    echo -e "${GREEN}"
+    echo "========================================="
+    echo "Webhook debugging setup complete!"
+    echo "========================================="
+    echo -e "${NC}"
+    echo "Next steps:"
+    echo "1. Open VS Code"
+    echo "2. Set breakpoints in webhook validation code:"
+    echo "   - pkg/webhook/utils/utils.go:141"
+    echo "   - pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler.go:74"
+    echo "3. Press F5 and select 'Debug Webhook Validation'"
+    echo "4. Wait for webhook server to start (port 9445)"
+    echo "5. Test with kubectl apply commands"
+    echo ""
+    echo -e "${YELLOW}Test command (should be rejected):${NC}"
+    echo 'kubectl apply -f test/webhook-test-invalid.yaml'
+    echo ""
+    echo -e "${GREEN}The webhook will reject ComponentDefinitions with non-existent CRDs${NC}"
+}
+
+# Main execution
+main() {
+    check_prerequisites
+    create_namespace
+    generate_certificates
+    create_k8s_secret
+    create_webhook_config
+    show_next_steps
+}
+
+# Run main function
+main "$@"
\ No newline at end of file
diff --git a/makefiles/develop.mk b/makefiles/develop.mk
index f957b844..585e613a 100644
--- a/makefiles/develop.mk
+++ b/makefiles/develop.mk
@@ -30,3 +30,75 @@ core-run: fmt vet manifests
 .PHONY: gen-cue
 gen-cue:
 	./hack/cuegen/cuegen.sh $(DIR) $(FLAGS)
+
+# ==============================================================================
+# Webhook Debug and Development Targets
+
+K3D_CLUSTER_NAME ?= kubevela-debug
+K3D_VERSION ?= v1.31.5
+
+## webhook-help: Show webhook debugging help
+.PHONY: webhook-help
+webhook-help:
+	@echo "=== KubeVela Webhook Debugging Guide ==="
+	@echo ""
+	@echo "Quick Start (recommended):"
+	@echo "  1. make webhook-debug-setup   # Complete setup"
+	@echo "  2. Start VS Code debugger (F5) with 'Debug Webhook Validation'"
+	@echo ""
+	@echo "Individual Commands:"
+	@echo "  make k3d-create               # Create k3d cluster"
+	@echo "  make k3d-delete               # Delete k3d cluster"
+	@echo "  make webhook-setup            # Setup webhook (certs + config)"
+	@echo "  make webhook-clean            # Clean up webhook setup"
+
+## k3d-create: Create a k3d cluster for debugging
+.PHONY: k3d-create
+k3d-create:
+	@echo "Creating k3d cluster: $(K3D_CLUSTER_NAME)"
+	@if k3d cluster list | grep -q "^$(K3D_CLUSTER_NAME)"; then \
+		echo "k3d cluster $(K3D_CLUSTER_NAME) already exists"; \
+	else \
+		k3d cluster create "$(K3D_CLUSTER_NAME)" \
+			--servers 1 \
+			--agents 1 \
+			--wait || (echo "Failed to create k3d cluster" && exit 1); \
+	fi
+	@kubectl config use-context "k3d-$(K3D_CLUSTER_NAME)"
+	@echo "k3d cluster $(K3D_CLUSTER_NAME) ready"
+
+## k3d-delete: Delete the k3d cluster
+.PHONY: k3d-delete
+k3d-delete:
+	@echo "Deleting k3d cluster: $(K3D_CLUSTER_NAME)"
+	@k3d cluster delete "$(K3D_CLUSTER_NAME)" || true
+
+## webhook-setup: Setup webhook certificates and configuration
+.PHONY: webhook-setup
+webhook-setup:
+	@echo "Setting up webhook certificates and configuration..."
+	@chmod +x hack/debug-webhook-setup.sh
+	@./hack/debug-webhook-setup.sh
+
+## webhook-debug-setup: Complete webhook debug environment setup
+.PHONY: webhook-debug-setup
+webhook-debug-setup:
+	@echo "Setting up complete webhook debug environment..."
+	@$(MAKE) k3d-create
+	@echo "Waiting for cluster to be ready..."
+	@sleep 5
+	@kubectl wait --for=condition=Ready nodes --all --timeout=60s || true
+	@echo "Installing KubeVela CRDs..."
+	@$(MAKE) manifests
+	@kubectl apply -f charts/vela-core/crds/ --validate=false
+	@echo "Setting up webhook..."
+	@$(MAKE) webhook-setup
+
+## webhook-clean: Clean up webhook debug environment
+.PHONY: webhook-clean
+webhook-clean:
+	@echo "Cleaning up webhook debug environment..."
+	@rm -rf k8s-webhook-server/
+	@kubectl delete secret webhook-server-cert -n vela-system --ignore-not-found
+	@kubectl delete validatingwebhookconfiguration kubevela-vela-core-admission --ignore-not-found
+	@echo "Webhook debug environment cleaned"
diff --git a/makefiles/e2e.mk b/makefiles/e2e.mk
index f3b2957d..3ae7d544 100644
--- a/makefiles/e2e.mk
+++ b/makefiles/e2e.mk
@@ -31,6 +31,7 @@ e2e-setup-core-wo-auth:
 		--set multicluster.clusterGateway.image.repository=ghcr.io/oam-dev/cluster-gateway \
 		--set admissionWebhooks.patch.image.repository=ghcr.io/oam-dev/kube-webhook-certgen/kube-webhook-certgen \
 		--set featureGates.enableCueValidation=true \
+		--set featureGates.validateResourcesExist=true \
 	    --wait kubevela ./charts/vela-core          \
 		--debug
 
@@ -48,10 +49,11 @@ e2e-setup-core-w-auth:
 	    ./charts/vela-core                              \
 	    --set authentication.enabled=true               \
 	    --set authentication.withUser=true              \
-	    --set authentication.groupPattern=*             \
+	    --set authentication.groupPattern='*'           \
 	    --set featureGates.zstdResourceTracker=true     \
 	    --set featureGates.zstdApplicationRevision=true \
 	    --set featureGates.validateComponentWhenSharding=true \
+	    --set featureGates.validateResourcesExist=true \
 	    --set multicluster.clusterGateway.enabled=true  \
 			--set multicluster.clusterGateway.image.repository=ghcr.io/oam-dev/cluster-gateway \
 			--set admissionWebhooks.patch.image.repository=ghcr.io/oam-dev/kube-webhook-certgen/kube-webhook-certgen \
@@ -85,6 +87,32 @@ e2e-test:
 	ginkgo -v ./test/e2e-test
 	@$(OK) tests pass
 
+# Run e2e tests with k3d and webhook validation
+.PHONY: e2e-test-local
+e2e-test-local:
+	# Create k3d cluster if needed
+	@k3d cluster create kubevela-debug --servers 1 --agents 1 || true
+	# Build and load image
+	docker build -t vela-core:e2e-test -f Dockerfile . --build-arg=VERSION=e2e-test --build-arg=GITVERSION=test
+	k3d image import vela-core:e2e-test -c kubevela-debug
+	# Deploy with Helm
+	kubectl delete validatingwebhookconfiguration kubevela-vela-core-admission 2>/dev/null || true
+	helm upgrade --install kubevela ./charts/vela-core \
+		--namespace vela-system --create-namespace \
+		--set image.repository=vela-core \
+		--set image.tag=e2e-test \
+		--set image.pullPolicy=IfNotPresent \
+		--set admissionWebhooks.enabled=true \
+		--set featureGates.enableCueValidation=true \
+		--set featureGates.validateResourcesExist=true \
+		--set applicationRevisionLimit=5 \
+		--set controllerArgs.reSyncPeriod=1m \
+		--wait --timeout 3m
+	# Run tests
+	ginkgo -v ./test/e2e-test
+	@$(OK) tests pass
+
+
 .PHONY: e2e-addon-test
 e2e-addon-test:
 	cp bin/vela /tmp/
diff --git a/pkg/addon/reader_gitlab.go b/pkg/addon/reader_gitlab.go
index d2a0bcb1..8fe46399 100644
--- a/pkg/addon/reader_gitlab.go
+++ b/pkg/addon/reader_gitlab.go
@@ -52,6 +52,9 @@ func (g GitLabItem) GetType() string {
 
 // GetPath get addon's sub item path
 func (g GitLabItem) GetPath() string {
+	if g.basePath == "" {
+		return g.path
+	}
 	return g.path[len(g.basePath)+1:]
 }
 
diff --git a/pkg/appfile/dryrun/dryrun.go b/pkg/appfile/dryrun/dryrun.go
index a564bc27..0f3bf174 100644
--- a/pkg/appfile/dryrun/dryrun.go
+++ b/pkg/appfile/dryrun/dryrun.go
@@ -109,12 +109,23 @@ func (d *Option) ValidateApp(ctx context.Context, filename string) error {
 	if err != nil {
 		return err
 	}
-
 	namespace := oamutil.GetDefinitionNamespaceWithCtx(ctx)
-
 	if namespace != "" {
-		app.SetNamespace(namespace)
-	} else if len(app.GetNamespace()) == 0 {
+		// Verify if namespace exists in cluster
+		ns := &corev1.Namespace{}
+		if err := d.Client.Get(ctx, client.ObjectKey{Name: namespace}, ns); err != nil {
+			if client.IgnoreNotFound(err) != nil {
+				// Non-NotFound error (RBAC, connectivity, etc.) - return the error
+				return err
+			}
+			// Namespace doesn't exist, set default
+			app.SetNamespace(corev1.NamespaceDefault)
+		} else {
+			// Namespace exists, use it
+			app.SetNamespace(namespace)
+		}
+	} else {
+		// Namespace is empty, set default
 		app.SetNamespace(corev1.NamespaceDefault)
 	}
 
diff --git a/pkg/controller/core.oam.dev/v1beta1/application/generator.go b/pkg/controller/core.oam.dev/v1beta1/application/generator.go
index ce742ac1..138d5dc5 100644
--- a/pkg/controller/core.oam.dev/v1beta1/application/generator.go
+++ b/pkg/controller/core.oam.dev/v1beta1/application/generator.go
@@ -508,6 +508,7 @@ func getComponentResources(ctx context.Context, manifest *types.ComponentManifes
 	return workload, traits, nil
 }
 
+// generateContextDataFromApp builds the process context for workflow (non-component) execution.
 func generateContextDataFromApp(app *v1beta1.Application, appRev string) velaprocess.ContextData {
 	data := velaprocess.ContextData{
 		Namespace:       app.Namespace,
@@ -519,5 +520,12 @@ func generateContextDataFromApp(app *v1beta1.Application, appRev string) velapro
 		data.WorkflowName = app.Annotations[oam.AnnotationWorkflowName]
 		data.PublishVersion = app.Annotations[oam.AnnotationPublishVersion]
 	}
+	// pass labels and annotations to workflow context
+	if len(app.Labels) > 0 {
+		data.AppLabels = app.Labels
+	}
+	if len(app.Annotations) > 0 {
+		data.AppAnnotations = app.Annotations
+	}
 	return data
 }
diff --git a/pkg/features/controller_features.go b/pkg/features/controller_features.go
index 841cca98..16a8518f 100644
--- a/pkg/features/controller_features.go
+++ b/pkg/features/controller_features.go
@@ -119,6 +119,10 @@ const (
 
 	// EnableApplicationStatusMetrics enable the collection and export of application status metrics and structured logging
 	EnableApplicationStatusMetrics = "EnableApplicationStatusMetrics"
+
+	// ValidateResourcesExist enables webhook validation to check if resource types referenced in
+	// ComponentDefinition/TraitDefinition/WorkflowStepDefinition/PolicyDefinition CUE templates exist in the cluster
+	ValidateResourcesExist = "ValidateResourcesExist"
 )
 
 var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
@@ -146,6 +150,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
 	DisableWorkflowContextConfigMapCache:          {Default: true, PreRelease: featuregate.Alpha},
 	EnableCueValidation:                           {Default: false, PreRelease: featuregate.Beta},
 	EnableApplicationStatusMetrics:                {Default: false, PreRelease: featuregate.Alpha},
+	ValidateResourcesExist:                        {Default: false, PreRelease: featuregate.Alpha},
 }
 
 func init() {
diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go
new file mode 100644
index 00000000..73c8d282
--- /dev/null
+++ b/pkg/logging/logger.go
@@ -0,0 +1,173 @@
+/*
+Copyright 2024 The KubeVela 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 logging provides structured logging utilities for KubeVela webhooks
+// with focus on request traceability and observability.
+package logging
+
+import (
+	"context"
+	"time"
+
+	"github.com/go-logr/logr"
+	"sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+// Structured logging field keys - consistent across all handlers for observability
+const (
+	// Core traceability fields
+	FieldRequestID = "requestID"  // Unique identifier for request correlation
+	FieldOperation = "operation"  // Webhook operation (CREATE/UPDATE/DELETE)
+	FieldHandler   = "handler"    // Handler processing the request
+	FieldStep      = "step"       // Current processing step
+	FieldDuration  = "durationMs" // Operation duration in milliseconds
+
+	// Resource identification
+	FieldName       = "name"       // Resource name
+	FieldNamespace  = "namespace"  // Resource namespace
+	FieldKind       = "kind"       // Resource kind
+	FieldGeneration = "generation" // Resource generation
+
+	// User context
+	FieldUserName = "user" // User making the request
+
+	// Error tracking
+	FieldError   = "error"   // Error indicator
+	FieldSuccess = "success" // Success indicator
+)
+
+// contextKey for storing values in context
+type contextKey struct{ name string }
+
+var (
+	requestIDKey = contextKey{name: "requestID"}
+	loggerKey    = contextKey{name: "logger"}
+)
+
+// Logger wraps logr.Logger with structured logging methods
+type Logger struct {
+	logr.Logger
+}
+
+// WithValues adds key-value pairs to the logger
+func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
+	return Logger{Logger: l.Logger.WithValues(keysAndValues...)}
+}
+
+// New creates a new Logger
+func New() Logger {
+	return Logger{Logger: log.Log}
+}
+
+// WithContext returns a Logger from context or creates a new one
+func WithContext(ctx context.Context) Logger {
+	if logger, ok := ctx.Value(loggerKey).(Logger); ok {
+		return logger
+	}
+	return New()
+}
+
+// IntoContext stores the Logger in context
+func (l Logger) IntoContext(ctx context.Context) context.Context {
+	return context.WithValue(ctx, loggerKey, l)
+}
+
+// WithRequestID stores request ID in context
+func WithRequestID(ctx context.Context, requestID string) context.Context {
+	if requestID == "" {
+		return ctx
+	}
+	return context.WithValue(ctx, requestIDKey, requestID)
+}
+
+// RequestIDFrom retrieves request ID from context
+func RequestIDFrom(ctx context.Context) (string, bool) {
+	id, ok := ctx.Value(requestIDKey).(string)
+	return id, ok && id != ""
+}
+
+// NewHandlerLogger creates a logger for webhook handlers with full request context
+func NewHandlerLogger(ctx context.Context, req admission.Request, handlerName string) Logger {
+	logger := New()
+
+	// Use admission UID as request ID for correlation
+	requestID := string(req.UID)
+	if rid, ok := RequestIDFrom(ctx); ok && rid != "" {
+		requestID = rid
+	}
+
+	// Build structured log with essential fields for observability
+	logger = logger.WithValues(
+		FieldRequestID, requestID,
+		FieldHandler, handlerName,
+		FieldOperation, req.Operation,
+		FieldKind, req.Kind.Kind,
+		FieldName, req.Name,
+		FieldNamespace, req.Namespace,
+		FieldUserName, req.UserInfo.Username,
+	)
+
+	return logger
+}
+
+// Helper methods that return the logger with values added
+// These don't log directly, so the actual logging call site is preserved
+
+// WithStep adds a step field to the logger
+func (l Logger) WithStep(step string) Logger {
+	return l.WithValues(FieldStep, step)
+}
+
+// WithSuccess adds success and duration fields to the logger
+func (l Logger) WithSuccess(success bool, startTime ...time.Time) Logger {
+	logger := l.WithValues(FieldSuccess, success)
+	if len(startTime) > 0 {
+		duration := time.Since(startTime[0])
+		logger = logger.WithValues(FieldDuration, duration.Milliseconds())
+	}
+	return logger
+}
+
+// WithError adds error context to the logger
+func (l Logger) WithError(err error) Logger {
+	return l.WithValues(FieldError, err.Error(), FieldSuccess, false)
+}
+
+// V returns a logger with verbosity level (0=info, 1=debug, 2=trace)
+func (l Logger) V(level int) Logger {
+	return Logger{Logger: l.Logger.V(level)}
+}
+
+// Debug logs debug message (verbosity 1)
+func (l Logger) Debug(msg string, keysAndValues ...interface{}) {
+	l.Logger.V(1).Info(msg, keysAndValues...)
+}
+
+// Trace logs trace message (verbosity 2)
+func (l Logger) Trace(msg string, keysAndValues ...interface{}) {
+	l.Logger.V(2).Info(msg, keysAndValues...)
+}
+
+// Info logs info message
+func (l Logger) Info(msg string, keysAndValues ...interface{}) {
+	l.Logger.Info(msg, keysAndValues...)
+}
+
+// Error logs error message
+func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
+	l.Logger.Error(err, msg, keysAndValues...)
+}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/application/validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/application/validating_handler.go
index cdc27857..b02f65d7 100644
--- a/pkg/webhook/core.oam.dev/v1beta1/application/validating_handler.go
+++ b/pkg/webhook/core.oam.dev/v1beta1/application/validating_handler.go
@@ -21,6 +21,7 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"time"
 
 	admissionv1 "k8s.io/api/admission/v1"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -31,6 +32,7 @@ import (
 
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
+	"github.com/oam-dev/kubevela/pkg/logging"
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 )
 
@@ -62,35 +64,78 @@ func mergeErrors(errs field.ErrorList) error {
 
 // Handle validate Application Spec here
 func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
+	startTime := time.Now()
+	ctx = logging.WithRequestID(ctx, string(req.UID))
+	logger := logging.NewHandlerLogger(ctx, req, "ApplicationValidator")
+
+	logger.WithStep("start").Info("Starting admission validation for Application resource", "operation", req.Operation, "applicationName", req.Name, "namespace", req.Namespace)
+
+	// Decode the application
 	app := &v1beta1.Application{}
 	if err := h.Decoder.Decode(req, app); err != nil {
-		return admission.Errored(http.StatusBadRequest, err)
+		logger.WithStep("decode").WithError(err).Error(err, "Unable to decode admission request payload into Application object - malformed request")
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("failed to decode: %w (requestUID=%s)", err, req.UID))
 	}
+
 	if req.Namespace != "" {
 		app.Namespace = req.Namespace
 	}
 
+	logger = logger.WithValues(logging.FieldGeneration, app.Generation)
+
+	workflowSteps := 0
+	if app.Spec.Workflow != nil {
+		workflowSteps = len(app.Spec.Workflow.Steps)
+	}
+
+	logger.WithStep("decode").Info("Successfully decoded Application from admission request",
+		"applicationName", app.Name,
+		"namespace", app.Namespace,
+		"componentCount", len(app.Spec.Components),
+		"policyCount", len(app.Spec.Policies),
+		"workflowSteps", workflowSteps)
+
 	ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
+
 	switch req.Operation {
 	case admissionv1.Create:
+		logger.WithStep("validate-create").Info("Validating Application creation - checking components, policies, and workflow configuration")
 		if allErrs := h.ValidateCreate(ctx, app, req); len(allErrs) > 0 {
-			// http.StatusUnprocessableEntity will NOT report any error descriptions
-			// to the client, use generic http.StatusBadRequest instead.
-			return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
+			mergedErr := mergeErrors(allErrs)
+			logger.WithStep("validate-create").WithError(mergedErr).Error(mergedErr, "Application creation validation failed - contains invalid components, policies, or workflow steps", "errorCount", len(allErrs), "applicationName", app.Name)
+			return admission.Errored(http.StatusBadRequest, fmt.Errorf("%w (requestUID=%s)", mergedErr, req.UID))
 		}
+		logger.WithStep("validate-create").WithSuccess(true).Info("Application creation validation completed successfully - all components, policies, and workflows are valid", "applicationName", app.Name)
+
 	case admissionv1.Update:
+		logger.WithStep("validate-update").Info("Validating Application update - comparing new configuration with existing state")
 		oldApp := &v1beta1.Application{}
 		if err := h.Decoder.DecodeRaw(req.AdmissionRequest.OldObject, oldApp); err != nil {
-			return admission.Errored(http.StatusBadRequest, simplifyError(err))
+			logger.WithStep("decode-old").WithError(err).Error(err, "Unable to decode previous Application state from admission request - cannot validate update")
+			return admission.Errored(http.StatusBadRequest, fmt.Errorf("%w (requestUID=%s)", simplifyError(err), req.UID))
 		}
+
+		logger = logger.WithValues("oldGeneration", oldApp.Generation)
+
 		if app.ObjectMeta.DeletionTimestamp.IsZero() {
 			if allErrs := h.ValidateUpdate(ctx, app, oldApp, req); len(allErrs) > 0 {
-				return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
+				mergedErr := mergeErrors(allErrs)
+				logger.WithStep("validate-update").WithError(mergedErr).Error(mergedErr, "Application update validation failed - new configuration contains invalid changes", "errorCount", len(allErrs), "applicationName", app.Name, "oldGeneration", oldApp.Generation, "newGeneration", app.Generation)
+				return admission.Errored(http.StatusBadRequest, fmt.Errorf("%w (requestUID=%s)", mergedErr, req.UID))
 			}
+			logger.WithStep("validate-update").WithSuccess(true).Info("Application update validation completed successfully - configuration changes are valid", "applicationName", app.Name, "generationChange", fmt.Sprintf("%d->%d", oldApp.Generation, app.Generation))
+		} else {
+			logger.WithStep("skip-validation").Info("Skipping Application validation - resource is being deleted and validation is not required", "reason", "deletion-in-progress", "deletionTimestamp", app.DeletionTimestamp)
 		}
+
+	case admissionv1.Delete:
+		logger.WithStep("skip-validation").Info("Skipping Application validation - DELETE operations do not require content validation", "reason", "delete-operation-no-validation-needed")
+
 	default:
-		// Do nothing for DELETE and CONNECT
+		logger.WithStep("skip-validation").Info("Skipping Application validation - operation type is not supported by validator", "operation", req.Operation, "reason", "only CREATE, UPDATE, and DELETE operations are handled")
 	}
+
+	logger.WithStep("complete").WithSuccess(true, startTime).Info("Application admission validation completed successfully - resource will be admitted", "applicationName", req.Name, "operation", req.Operation, "namespace", req.Namespace)
 	return admission.ValidationResponse(true, "")
 }
 
diff --git a/pkg/webhook/core.oam.dev/v1beta1/application/validation.go b/pkg/webhook/core.oam.dev/v1beta1/application/validation.go
index 6d2842c0..a600cba1 100644
--- a/pkg/webhook/core.oam.dev/v1beta1/application/validation.go
+++ b/pkg/webhook/core.oam.dev/v1beta1/application/validation.go
@@ -33,7 +33,6 @@ import (
 
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	"github.com/oam-dev/kubevela/pkg/appfile"
-	"github.com/oam-dev/kubevela/pkg/auth"
 	"github.com/oam-dev/kubevela/pkg/features"
 	"github.com/oam-dev/kubevela/pkg/oam"
 )
@@ -319,8 +318,8 @@ func (h *ValidatingHandler) validateDefinitions(
 
 // ValidateDefinitionPermissions validates that the user has permissions to access all definition types
 func (h *ValidatingHandler) ValidateDefinitionPermissions(ctx context.Context, app *v1beta1.Application, req admission.Request) field.ErrorList {
-	// Requires both authentication and definition validation enabled
-	if !auth.AuthenticationWithUser || !utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateDefinitionPermissions) {
+	// Check if definition validation is enabled
+	if !utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateDefinitionPermissions) {
 		return nil
 	}
 
diff --git a/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler.go
new file mode 100644
index 00000000..62bc20f6
--- /dev/null
+++ b/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler.go
@@ -0,0 +1,171 @@
+/*
+ Copyright 2021. The KubeVela 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 componentdefinition
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	admissionv1 "k8s.io/api/admission/v1"
+	"k8s.io/apimachinery/pkg/api/meta"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/logging"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	"github.com/oam-dev/kubevela/pkg/oam/util"
+	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
+)
+
+var componentDefGVR = v1beta1.ComponentDefinitionGVR
+
+// ValidatingHandler handles validation of component definition
+type ValidatingHandler struct {
+	// Decoder decodes object
+	Decoder admission.Decoder
+	Client  client.Client
+}
+
+var _ admission.Handler = &ValidatingHandler{}
+
+// Handle validate ComponentDefinition Spec here
+func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
+	startTime := time.Now()
+	ctx = logging.WithRequestID(ctx, string(req.UID))
+	logger := logging.NewHandlerLogger(ctx, req, "ComponentDefinitionValidator")
+
+	// Using the logger methods directly will show the correct file location
+	logger.WithStep("start").Info("Starting admission validation for ComponentDefinition resource", "operation", req.Operation, "resourceVersion", req.Kind.Version)
+
+	obj := &v1beta1.ComponentDefinition{}
+	if req.Resource.String() != componentDefGVR.String() {
+		err := fmt.Errorf("expect resource to be %s", componentDefGVR)
+		logger.WithStep("resource-check").WithError(err).Error(err, "Admission request targets unexpected resource type - rejecting request",
+			"expected", componentDefGVR.String(),
+			"actual", req.Resource.String(),
+			"operation", req.Operation)
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
+		if err := h.Decoder.Decode(req, obj); err != nil {
+			logger.WithStep("decode").WithError(err).Error(err, "Unable to decode admission request payload into ComponentDefinition object - malformed request")
+			return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+
+		// Add definition-specific fields to logger
+		if obj.Spec.Version != "" {
+			logger = logger.WithValues("version", obj.Spec.Version)
+		}
+		logger.WithStep("decode").Info("Successfully decoded ComponentDefinition from admission request",
+			"definitionName", obj.Name,
+			"namespace", obj.Namespace,
+			"workloadType", obj.Spec.Workload.Type,
+			"hasSchematic", obj.Spec.Schematic != nil)
+
+		// Validate workload
+		if err := ValidateWorkload(h.Client.RESTMapper(), obj); err != nil {
+			logger.WithStep("validate-workload").WithError(err).Error(err, "ComponentDefinition workload configuration is invalid - type and definition must be consistent")
+			return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+		logger.WithStep("validate-workload").Info("ComponentDefinition workload configuration validated successfully", "workloadType", obj.Spec.Workload.Type)
+
+		// Validate CUE template
+		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
+			logger.WithStep("validate-cue").Info("Validating CUE template syntax and semantics for ComponentDefinition schematic")
+
+			if err := webhookutils.ValidateCuexTemplate(ctx, obj.Spec.Schematic.CUE.Template); err != nil {
+				logger.WithStep("validate-cue").WithError(err).Error(err, "CUE template contains syntax errors or invalid constructs - template compilation failed")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+
+			if err := webhookutils.ValidateOutputResourcesExist(obj.Spec.Schematic.CUE.Template, h.Client.RESTMapper(), obj); err != nil {
+				logger.WithStep("validate-output-resources").WithError(err).Error(err, "CUE template references output resources that don't exist in cluster - unknown resource types detected")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-cue").WithSuccess(true).Info("CUE template validation completed successfully - template is syntactically correct and all output resources exist")
+		}
+
+		// Validate semantic version
+		if obj.Spec.Version != "" {
+			if err := webhookutils.ValidateSemanticVersion(obj.Spec.Version); err != nil {
+				logger.WithStep("validate-version").WithError(err).Error(err, "ComponentDefinition version does not follow semantic versioning format (x.y.z)", "version", obj.Spec.Version, "expectedFormat", "x.y.z")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-version").Info("ComponentDefinition version follows semantic versioning format", "version", obj.Spec.Version)
+		}
+
+		// Validate revision
+		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
+		if len(revisionName) != 0 {
+			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
+			if err := webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName}); err != nil {
+				logger.WithStep("validate-revision").WithError(err).Error(err, "ComponentDefinition revision conflicts with existing revision or has invalid format", "revisionName", revisionName, "expectedRevisionName", fmt.Sprintf("%s-v%s", obj.Name, revisionName))
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-revision").Info("ComponentDefinition revision validation completed - no conflicts detected", "revisionName", revisionName)
+		}
+
+		// Check version conflicts
+		if err := webhookutils.ValidateMultipleDefVersionsNotPresent(obj.Spec.Version, revisionName, obj.Kind); err != nil {
+			logger.WithStep("validate-version-conflict").WithError(err).Error(err, "ComponentDefinition has conflicting version specifications - cannot have both spec.version and revision annotation", "specVersion", obj.Spec.Version, "revisionName", revisionName)
+			return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+
+		// Log successful completion
+		logger.WithStep("complete").WithSuccess(true, startTime).Info("ComponentDefinition admission validation completed successfully - resource is valid and will be admitted", "definitionName", obj.Name, "operation", req.Operation)
+	} else {
+		logger.WithStep("skip-validation").Info("Skipping ComponentDefinition validation - operation does not require validation", "operation", req.Operation, "reason", "only CREATE and UPDATE operations are validated")
+	}
+	return admission.ValidationResponse(true, "")
+}
+
+// RegisterValidatingHandler will register ComponentDefinition validation to webhook
+func RegisterValidatingHandler(mgr manager.Manager) {
+	server := mgr.GetWebhookServer()
+	server.Register("/validating-core-oam-dev-v1beta1-componentdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
+		Client:  mgr.GetClient(),
+		Decoder: admission.NewDecoder(mgr.GetScheme()),
+	}})
+}
+
+// ValidateWorkload validates whether the Workload field is valid
+func ValidateWorkload(mapper meta.RESTMapper, cd *v1beta1.ComponentDefinition) error {
+
+	// If the Type and Definition are all empty, it will be rejected.
+	if cd.Spec.Workload.Type == "" && cd.Spec.Workload.Definition == (common.WorkloadGVK{}) {
+		return fmt.Errorf("neither the type nor the definition of the workload field in the ComponentDefinition %s can be empty", cd.Name)
+	}
+
+	// if Type and Definitiondon‘t point to the same workloaddefinition, it will be rejected.
+	if cd.Spec.Workload.Type != "" && cd.Spec.Workload.Definition != (common.WorkloadGVK{}) {
+		defRef, err := util.ConvertWorkloadGVK2Definition(mapper, cd.Spec.Workload.Definition)
+		if err != nil {
+			return err
+		}
+		if defRef.Name != cd.Spec.Workload.Type {
+			return fmt.Errorf("the type and the definition of the workload field in ComponentDefinition %s should represent the same workload", cd.Name)
+		}
+	}
+	return nil
+}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler.go
deleted file mode 100644
index 8622e21a..00000000
--- a/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler.go
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- Copyright 2021. The KubeVela 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 componentdefinition
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	admissionv1 "k8s.io/api/admission/v1"
-	"k8s.io/apimachinery/pkg/api/meta"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/webhook"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-	"github.com/oam-dev/kubevela/pkg/oam"
-	"github.com/oam-dev/kubevela/pkg/oam/util"
-	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
-)
-
-var componentDefGVR = v1beta1.ComponentDefinitionGVR
-
-// ValidatingHandler handles validation of component definition
-type ValidatingHandler struct {
-	// Decoder decodes object
-	Decoder admission.Decoder
-	Client  client.Client
-}
-
-var _ admission.Handler = &ValidatingHandler{}
-
-// Handle validate ComponentDefinition Spec here
-func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
-	obj := &v1beta1.ComponentDefinition{}
-	if req.Resource.String() != componentDefGVR.String() {
-		return admission.Errored(http.StatusBadRequest, fmt.Errorf("expect resource to be %s", componentDefGVR))
-	}
-
-	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
-		err := h.Decoder.Decode(req, obj)
-		if err != nil {
-			return admission.Errored(http.StatusBadRequest, err)
-		}
-		err = ValidateWorkload(h.Client.RESTMapper(), obj)
-		if err != nil {
-			return admission.Denied(err.Error())
-		}
-
-		// validate cueTemplate
-		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
-			err = webhookutils.ValidateCuexTemplate(ctx, obj.Spec.Schematic.CUE.Template)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		if obj.Spec.Version != "" {
-			err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
-		if len(revisionName) != 0 {
-			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
-			err = webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName})
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		version := obj.Spec.Version
-		err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
-		if err != nil {
-			return admission.Denied(err.Error())
-		}
-	}
-	return admission.ValidationResponse(true, "")
-}
-
-// RegisterValidatingHandler will register ComponentDefinition validation to webhook
-func RegisterValidatingHandler(mgr manager.Manager) {
-	server := mgr.GetWebhookServer()
-	server.Register("/validating-core-oam-dev-v1beta1-componentdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
-		Client:  mgr.GetClient(),
-		Decoder: admission.NewDecoder(mgr.GetScheme()),
-	}})
-}
-
-// ValidateWorkload validates whether the Workload field is valid
-func ValidateWorkload(mapper meta.RESTMapper, cd *v1beta1.ComponentDefinition) error {
-
-	// If the Type and Definition are all empty, it will be rejected.
-	if cd.Spec.Workload.Type == "" && cd.Spec.Workload.Definition == (common.WorkloadGVK{}) {
-		return fmt.Errorf("neither the type nor the definition of the workload field in the ComponentDefinition %s can be empty", cd.Name)
-	}
-
-	// if Type and Definitiondon‘t point to the same workloaddefinition, it will be rejected.
-	if cd.Spec.Workload.Type != "" && cd.Spec.Workload.Definition != (common.WorkloadGVK{}) {
-		defRef, err := util.ConvertWorkloadGVK2Definition(mapper, cd.Spec.Workload.Definition)
-		if err != nil {
-			return err
-		}
-		if defRef.Name != cd.Spec.Workload.Type {
-			return fmt.Errorf("the type and the definition of the workload field in ComponentDefinition %s should represent the same workload", cd.Name)
-		}
-	}
-	return nil
-}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/policydefinition/policy_definition_validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/policydefinition/policy_definition_validating_handler.go
new file mode 100644
index 00000000..5246e5c2
--- /dev/null
+++ b/pkg/webhook/core.oam.dev/v1beta1/policydefinition/policy_definition_validating_handler.go
@@ -0,0 +1,129 @@
+/*
+ Copyright 2021. The KubeVela 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 policydefinition
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	admissionv1 "k8s.io/api/admission/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/logging"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
+)
+
+var policyDefGVR = v1beta1.PolicyDefinitionGVR
+
+// ValidatingHandler handles validation of policy definition
+type ValidatingHandler struct {
+	// Decoder decodes object
+	Decoder admission.Decoder
+	Client  client.Client
+}
+
+var _ admission.Handler = &ValidatingHandler{}
+
+// Handle validate component definition
+func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
+	startTime := time.Now()
+	ctx = logging.WithRequestID(ctx, string(req.UID))
+	logger := logging.NewHandlerLogger(ctx, req, "PolicyDefinitionValidator")
+
+	logger.WithStep("start").Info("Starting admission validation for PolicyDefinition resource", "operation", req.Operation, "resourceVersion", req.Kind.Version)
+
+	obj := &v1beta1.PolicyDefinition{}
+	if req.Resource.String() != policyDefGVR.String() {
+		err := fmt.Errorf("expect resource to be %s", policyDefGVR)
+		logger.WithStep("resource-check").WithError(err).Error(err, "Admission request targets unexpected resource type - rejecting request",
+			"expected", policyDefGVR.String(),
+			"actual", req.Resource.String(),
+			"operation", req.Operation)
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
+		if err := h.Decoder.Decode(req, obj); err != nil {
+			logger.WithStep("decode").WithError(err).Error(err, "Unable to decode admission request payload into PolicyDefinition object - malformed request")
+			return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+		if obj.Spec.Version != "" {
+			logger = logger.WithValues("version", obj.Spec.Version)
+		}
+		logger.WithStep("decode").Info("Successfully decoded PolicyDefinition from admission request",
+			"definitionName", obj.Name,
+			"namespace", obj.Namespace,
+			"hasSchematic", obj.Spec.Schematic != nil,
+			"version", obj.Spec.Version)
+
+		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
+			logger.WithStep("validate-cue").Info("Validating CUE template syntax and semantics for PolicyDefinition schematic")
+			if err := webhookutils.ValidateCueTemplate(obj.Spec.Schematic.CUE.Template); err != nil {
+				logger.WithStep("validate-cue").WithError(err).Error(err, "CUE template contains syntax errors or invalid constructs - template compilation failed")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			if err := webhookutils.ValidateOutputResourcesExist(obj.Spec.Schematic.CUE.Template, h.Client.RESTMapper(), obj); err != nil {
+				logger.WithStep("validate-output-resources").WithError(err).Error(err, "CUE template references output resources that don't exist in cluster - unknown resource types detected")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-cue").WithSuccess(true).Info("CUE template validation completed successfully - template is syntactically correct and all output resources exist")
+		}
+
+		if obj.Spec.Version != "" {
+			if err := webhookutils.ValidateSemanticVersion(obj.Spec.Version); err != nil {
+				logger.WithStep("validate-version").WithError(err).Error(err, "PolicyDefinition version does not follow semantic versioning format (x.y.z)", "version", obj.Spec.Version, "expectedFormat", "x.y.z")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-version").Info("PolicyDefinition version follows semantic versioning format", "version", obj.Spec.Version)
+		}
+
+		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
+		if len(revisionName) != 0 {
+			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
+			if err := webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName}); err != nil {
+				logger.WithStep("validate-revision").WithError(err).Error(err, "PolicyDefinition revision conflicts with existing revision or has invalid format", "revisionName", revisionName, "expectedRevisionName", fmt.Sprintf("%s-v%s", obj.Name, revisionName))
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-revision").Info("PolicyDefinition revision validation completed - no conflicts detected", "revisionName", revisionName)
+		}
+
+		if err := webhookutils.ValidateMultipleDefVersionsNotPresent(obj.Spec.Version, revisionName, obj.Kind); err != nil {
+			logger.WithStep("validate-version-conflict").WithError(err).Error(err, "PolicyDefinition has conflicting version specifications - cannot have both spec.version and revision annotation", "specVersion", obj.Spec.Version, "revisionName", revisionName)
+			return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+		logger.WithStep("complete").WithSuccess(true, startTime).Info("PolicyDefinition admission validation completed successfully - resource is valid and will be admitted", "definitionName", obj.Name, "operation", req.Operation)
+	} else {
+		logger.WithStep("skip-validation").Info("Skipping PolicyDefinition validation - operation does not require validation", "operation", req.Operation, "reason", "only CREATE and UPDATE operations are validated")
+	}
+	return admission.ValidationResponse(true, "")
+}
+
+// RegisterValidatingHandler will register ComponentDefinition validation to webhook
+func RegisterValidatingHandler(mgr manager.Manager) {
+	server := mgr.GetWebhookServer()
+	server.Register("/validating-core-oam-dev-v1beta1-policydefinitions", &webhook.Admission{Handler: &ValidatingHandler{
+		Client:  mgr.GetClient(),
+		Decoder: admission.NewDecoder(mgr.GetScheme()),
+	}})
+}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler.go
deleted file mode 100644
index 464c7e85..00000000
--- a/pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler.go
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- Copyright 2021. The KubeVela 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 policydefinition
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	admissionv1 "k8s.io/api/admission/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/webhook"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-	"github.com/oam-dev/kubevela/pkg/oam"
-	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
-)
-
-var policyDefGVR = v1beta1.PolicyDefinitionGVR
-
-// ValidatingHandler handles validation of policy definition
-type ValidatingHandler struct {
-	// Decoder decodes object
-	Decoder admission.Decoder
-	Client  client.Client
-}
-
-var _ admission.Handler = &ValidatingHandler{}
-
-// Handle validate component definition
-func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
-	obj := &v1beta1.PolicyDefinition{}
-	if req.Resource.String() != policyDefGVR.String() {
-		return admission.Errored(http.StatusBadRequest, fmt.Errorf("expect resource to be %s", policyDefGVR))
-	}
-
-	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
-		err := h.Decoder.Decode(req, obj)
-		if err != nil {
-			return admission.Errored(http.StatusBadRequest, err)
-		}
-
-		// validate cueTemplate
-		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
-			err = webhookutils.ValidateCueTemplate(obj.Spec.Schematic.CUE.Template)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		if obj.Spec.Version != "" {
-			err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
-		if len(revisionName) != 0 {
-			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
-			err = webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName})
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		version := obj.Spec.Version
-		err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
-		if err != nil {
-			return admission.Denied(err.Error())
-		}
-	}
-	return admission.ValidationResponse(true, "")
-}
-
-// RegisterValidatingHandler will register ComponentDefinition validation to webhook
-func RegisterValidatingHandler(mgr manager.Manager) {
-	server := mgr.GetWebhookServer()
-	server.Register("/validating-core-oam-dev-v1beta1-policydefinitions", &webhook.Admission{Handler: &ValidatingHandler{
-		Client:  mgr.GetClient(),
-		Decoder: admission.NewDecoder(mgr.GetScheme()),
-	}})
-}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler.go
new file mode 100644
index 00000000..105cda25
--- /dev/null
+++ b/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler.go
@@ -0,0 +1,188 @@
+/*
+Copyright 2021 The KubeVela 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 traitdefinition
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/pkg/errors"
+	admissionv1 "k8s.io/api/admission/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/appfile"
+	controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
+	"github.com/oam-dev/kubevela/pkg/logging"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
+)
+
+const (
+	errValidateDefRef = "error occurs when validating definition reference"
+
+	failInfoDefRefOmitted = "if definition reference is omitted, patch or output with GVK is required"
+)
+
+var traitDefGVR = v1beta1.TraitDefinitionGVR
+
+// ValidatingHandler handles validation of trait definition
+type ValidatingHandler struct {
+	Client client.Client
+
+	// Decoder decodes object
+	Decoder admission.Decoder
+	// Validators validate objects
+	Validators []TraitDefValidator
+}
+
+// TraitDefValidator validate trait definition
+type TraitDefValidator interface {
+	Validate(context.Context, v1beta1.TraitDefinition) error
+}
+
+// TraitDefValidatorFn implements TraitDefValidator
+type TraitDefValidatorFn func(context.Context, v1beta1.TraitDefinition) error
+
+// Validate implements TraitDefValidator method
+func (fn TraitDefValidatorFn) Validate(ctx context.Context, td v1beta1.TraitDefinition) error {
+	return fn(ctx, td)
+}
+
+var _ admission.Handler = &ValidatingHandler{}
+
+// Handle validate trait definition
+func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
+	startTime := time.Now()
+	ctx = logging.WithRequestID(ctx, string(req.UID))
+	logger := logging.NewHandlerLogger(ctx, req, "TraitDefinitionValidator")
+
+	logger.WithStep("start").Info("Starting admission validation for TraitDefinition resource", "operation", req.Operation, "resourceVersion", req.Kind.Version)
+
+	obj := &v1beta1.TraitDefinition{}
+	if req.Resource.String() != traitDefGVR.String() {
+		err := fmt.Errorf("expect resource to be %s", traitDefGVR)
+		logger.WithStep("resource-check").WithError(err).Error(err, "Admission request targets unexpected resource type - rejecting request",
+			"expected", traitDefGVR.String(),
+			"actual", req.Resource.String(),
+			"operation", req.Operation)
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
+		if err := h.Decoder.Decode(req, obj); err != nil {
+			logger.WithStep("decode").WithError(err).Error(err, "Unable to decode admission request payload into TraitDefinition object - malformed request")
+			return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+		if obj.Spec.Version != "" {
+			logger = logger.WithValues("version", obj.Spec.Version)
+		}
+		logger.WithStep("decode").Info("Successfully decoded TraitDefinition from admission request",
+			"definitionName", obj.Name,
+			"namespace", obj.Namespace,
+			"hasReference", len(obj.Spec.Reference.Name) > 0,
+			"hasSchematic", obj.Spec.Schematic != nil)
+
+		for i, validator := range h.Validators {
+			if err := validator.Validate(ctx, *obj); err != nil {
+				logger.WithStep(fmt.Sprintf("validator-%d", i)).WithError(err).Error(err, "TraitDefinition custom validator failed - definition does not meet validation requirements", "validatorIndex", i)
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+		}
+
+		// validate cueTemplate
+		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
+			logger.WithStep("validate-cue").Info("Validating CUE template syntax and semantics for TraitDefinition schematic")
+			if err := webhookutils.ValidateCuexTemplate(ctx, obj.Spec.Schematic.CUE.Template); err != nil {
+				logger.WithStep("validate-cue").WithError(err).Error(err, "CUE template contains syntax errors or invalid constructs - template compilation failed")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			if err := webhookutils.ValidateOutputResourcesExist(obj.Spec.Schematic.CUE.Template, h.Client.RESTMapper(), obj); err != nil {
+				logger.WithStep("validate-output-resources").WithError(err).Error(err, "CUE template references output resources that don't exist in cluster - unknown resource types detected")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+			logger.WithStep("validate-cue").WithSuccess(true).Info("CUE template validation completed successfully - template is syntactically correct and all output resources exist")
+		}
+
+		if obj.Spec.Version != "" {
+			if err := webhookutils.ValidateSemanticVersion(obj.Spec.Version); err != nil {
+				logger.WithStep("validate-version").WithError(err).Error(err, "TraitDefinition version does not follow semantic versioning format (x.y.z)", "version", obj.Spec.Version, "expectedFormat", "x.y.z")
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+		}
+
+		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
+		if len(revisionName) != 0 {
+			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
+			if err := webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName}); err != nil {
+				logger.WithStep("validate-revision").WithError(err).Error(err, "TraitDefinition revision conflicts with existing revision or has invalid format", "revisionName", revisionName, "expectedRevisionName", fmt.Sprintf("%s-v%s", obj.Name, revisionName))
+				return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+			}
+		}
+
+		version := obj.Spec.Version
+		if err := webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind); err != nil {
+			logger.WithStep("validate-version-conflict").WithError(err).Error(err, "TraitDefinition has conflicting version specifications - cannot have both spec.version and revision annotation", "specVersion", version, "revisionName", revisionName)
+			return admission.Denied(fmt.Sprintf("%s (requestUID=%s)", err.Error(), req.UID))
+		}
+		logger.WithStep("complete").WithSuccess(true, startTime).Info("TraitDefinition admission validation completed successfully - resource is valid and will be admitted", "definitionName", obj.Name, "operation", req.Operation)
+	} else {
+		logger.WithStep("skip-validation").Info("Skipping TraitDefinition validation - operation does not require validation", "operation", req.Operation, "reason", "only CREATE and UPDATE operations are validated")
+	}
+	return admission.ValidationResponse(true, "")
+}
+
+// RegisterValidatingHandler will register TraitDefinition validation to webhook
+func RegisterValidatingHandler(mgr manager.Manager, _ controller.Args) {
+	server := mgr.GetWebhookServer()
+	server.Register("/validating-core-oam-dev-v1beta1-traitdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
+		Client:  mgr.GetClient(),
+		Decoder: admission.NewDecoder(mgr.GetScheme()),
+		Validators: []TraitDefValidator{
+			TraitDefValidatorFn(ValidateDefinitionReference),
+			// add more validators here
+		},
+	}})
+}
+
+// ValidateDefinitionReference validates whether the trait definition is valid if
+// its `.spec.reference` field is unset.
+// It's valid if
+// it has at least one output, and all outputs must have GVK
+// or it has no output but has a patch
+// or it has a patch and outputs, and all outputs must have GVK
+// TODO(roywang) currently we only validate whether it contains CUE template.
+// Further validation, e.g., output with GVK, valid patch, etc, remains to be done.
+func ValidateDefinitionReference(_ context.Context, td v1beta1.TraitDefinition) error {
+	if len(td.Spec.Reference.Name) > 0 {
+		return nil
+	}
+	capability, err := appfile.ConvertTemplateJSON2Object(td.Name, td.Spec.Extension, td.Spec.Schematic)
+	if err != nil {
+		return errors.WithMessage(err, errValidateDefRef)
+	}
+	if capability.CueTemplate == "" {
+		return errors.New(failInfoDefRefOmitted)
+
+	}
+	return nil
+}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler.go
deleted file mode 100644
index a850ae30..00000000
--- a/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler.go
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-Copyright 2021 The KubeVela 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 traitdefinition
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	"github.com/pkg/errors"
-	admissionv1 "k8s.io/api/admission/v1"
-	"k8s.io/klog/v2"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/webhook"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-	"github.com/oam-dev/kubevela/pkg/appfile"
-	controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
-	"github.com/oam-dev/kubevela/pkg/oam"
-	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
-)
-
-const (
-	errValidateDefRef = "error occurs when validating definition reference"
-
-	failInfoDefRefOmitted = "if definition reference is omitted, patch or output with GVK is required"
-)
-
-var traitDefGVR = v1beta1.TraitDefinitionGVR
-
-// ValidatingHandler handles validation of trait definition
-type ValidatingHandler struct {
-	Client client.Client
-
-	// Decoder decodes object
-	Decoder admission.Decoder
-	// Validators validate objects
-	Validators []TraitDefValidator
-}
-
-// TraitDefValidator validate trait definition
-type TraitDefValidator interface {
-	Validate(context.Context, v1beta1.TraitDefinition) error
-}
-
-// TraitDefValidatorFn implements TraitDefValidator
-type TraitDefValidatorFn func(context.Context, v1beta1.TraitDefinition) error
-
-// Validate implements TraitDefValidator method
-func (fn TraitDefValidatorFn) Validate(ctx context.Context, td v1beta1.TraitDefinition) error {
-	return fn(ctx, td)
-}
-
-var _ admission.Handler = &ValidatingHandler{}
-
-// Handle validate trait definition
-func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
-	obj := &v1beta1.TraitDefinition{}
-	if req.Resource.String() != traitDefGVR.String() {
-		return admission.Errored(http.StatusBadRequest, fmt.Errorf("expect resource to be %s", traitDefGVR))
-	}
-
-	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
-		err := h.Decoder.Decode(req, obj)
-		if err != nil {
-			return admission.Errored(http.StatusBadRequest, err)
-		}
-		klog.Info("validating ", " name: ", obj.Name, " operation: ", string(req.Operation))
-		for _, validator := range h.Validators {
-			if err := validator.Validate(ctx, *obj); err != nil {
-				klog.Info("validation failed ", " name: ", obj.Name, " errMsgi: ", err.Error())
-				return admission.Denied(err.Error())
-			}
-		}
-
-		// validate cueTemplate
-		if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
-			err = webhookutils.ValidateCuexTemplate(ctx, obj.Spec.Schematic.CUE.Template)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		if obj.Spec.Version != "" {
-			err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
-		if len(revisionName) != 0 {
-			defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
-			err = webhookutils.ValidateDefinitionRevision(ctx, h.Client, obj, client.ObjectKey{Namespace: obj.Namespace, Name: defRevName})
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-
-		version := obj.Spec.Version
-		err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
-		if err != nil {
-			return admission.Denied(err.Error())
-		}
-		klog.Info("validation passed ", " name: ", obj.Name, " operation: ", string(req.Operation))
-	}
-	return admission.ValidationResponse(true, "")
-}
-
-// RegisterValidatingHandler will register TraitDefinition validation to webhook
-func RegisterValidatingHandler(mgr manager.Manager, _ controller.Args) {
-	server := mgr.GetWebhookServer()
-	server.Register("/validating-core-oam-dev-v1beta1-traitdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
-		Client:  mgr.GetClient(),
-		Decoder: admission.NewDecoder(mgr.GetScheme()),
-		Validators: []TraitDefValidator{
-			TraitDefValidatorFn(ValidateDefinitionReference),
-			// add more validators here
-		},
-	}})
-}
-
-// ValidateDefinitionReference validates whether the trait definition is valid if
-// its `.spec.reference` field is unset.
-// It's valid if
-// it has at least one output, and all outputs must have GVK
-// or it has no output but has a patch
-// or it has a patch and outputs, and all outputs must have GVK
-// TODO(roywang) currently we only validate whether it contains CUE template.
-// Further validation, e.g., output with GVK, valid patch, etc, remains to be done.
-func ValidateDefinitionReference(_ context.Context, td v1beta1.TraitDefinition) error {
-	if len(td.Spec.Reference.Name) > 0 {
-		return nil
-	}
-	capability, err := appfile.ConvertTemplateJSON2Object(td.Name, td.Spec.Extension, td.Spec.Schematic)
-	if err != nil {
-		return errors.WithMessage(err, errValidateDefRef)
-	}
-	if capability.CueTemplate == "" {
-		return errors.New(failInfoDefRefOmitted)
-
-	}
-	return nil
-}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/validating_handler.go
deleted file mode 100644
index f430d368..00000000
--- a/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/validating_handler.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright 2024 The KubeVela 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 workflowstepdefinition
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	admissionv1 "k8s.io/api/admission/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/webhook"
-	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-	"github.com/oam-dev/kubevela/pkg/oam"
-	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
-)
-
-var workflowStepDefGVR = v1beta1.WorkflowStepDefinitionGVR
-
-// ValidatingHandler handles validation of workflow step definition
-type ValidatingHandler struct {
-	// Decoder decodes object
-	Decoder admission.Decoder
-	Client  client.Client
-}
-
-// InjectClient injects the client into the ValidatingHandler
-func (h *ValidatingHandler) InjectClient(c client.Client) error {
-	h.Client = c
-	return nil
-}
-
-// InjectDecoder injects the decoder into the ValidatingHandler
-func (h *ValidatingHandler) InjectDecoder(d admission.Decoder) error {
-	h.Decoder = d
-	return nil
-}
-
-// Handle validate WorkflowStepDefinition Spec here
-func (h *ValidatingHandler) Handle(_ context.Context, req admission.Request) admission.Response {
-	obj := &v1beta1.WorkflowStepDefinition{}
-	if req.Resource.String() != workflowStepDefGVR.String() {
-		return admission.Errored(http.StatusBadRequest, fmt.Errorf("expect resource to be %s", workflowStepDefGVR))
-	}
-
-	if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
-		err := h.Decoder.Decode(req, obj)
-		if err != nil {
-			return admission.Errored(http.StatusBadRequest, err)
-		}
-
-		if obj.Spec.Version != "" {
-			err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
-			if err != nil {
-				return admission.Denied(err.Error())
-			}
-		}
-		revisionName := obj.Annotations[oam.AnnotationDefinitionRevisionName]
-		version := obj.Spec.Version
-		err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
-		if err != nil {
-			return admission.Denied(err.Error())
-		}
-	}
-	return admission.ValidationResponse(true, "")
-}
-
-// RegisterValidatingHandler will register WorkflowStepDefinition validation to webhook
-func RegisterValidatingHandler(mgr manager.Manager) {
-	server := mgr.GetWebhookServer()
-	server.Register("/validating-core-oam-dev-v1beta1-workflowstepdefinitions", &webhook.Admission{Handler: &ValidatingHandler{}})
-}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler.go b/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler.go
new file mode 100644
index 00000000..e159af12
--- /dev/null
+++ b/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler.go
@@ -0,0 +1,148 @@
+/*
+Copyright 2024 The KubeVela 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 workflowstepdefinition provides admission control validation
+// for WorkflowStepDefinition resources in KubeVela.
+package workflowstepdefinition
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	admissionv1 "k8s.io/api/admission/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/logging"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
+)
+
+const (
+	// ValidationWebhookPath defines the HTTP path for the validation webhook
+	ValidationWebhookPath = "/validating-core-oam-dev-v1beta1-workflowstepdefinitions"
+)
+
+var (
+	workflowStepDefGVR = v1beta1.WorkflowStepDefinitionGVR
+)
+
+// ValidatingHandler handles validation of WorkflowStepDefinition resources.
+type ValidatingHandler struct {
+	Decoder admission.Decoder
+	Client  client.Client
+}
+
+// InjectClient injects the Kubernetes client into the handler.
+func (h *ValidatingHandler) InjectClient(c client.Client) error {
+	h.Client = c
+	return nil
+}
+
+// InjectDecoder injects the admission decoder into the handler.
+func (h *ValidatingHandler) InjectDecoder(d admission.Decoder) error {
+	h.Decoder = d
+	return nil
+}
+
+// Handle validates WorkflowStepDefinition resources during admission control.
+func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
+	startTime := time.Now()
+	ctx = logging.WithRequestID(ctx, string(req.UID))
+	logger := logging.NewHandlerLogger(ctx, req, "WorkflowStepDefinitionValidator")
+
+	logger.WithStep("start").Info("Starting admission validation for WorkflowStepDefinition resource", "operation", req.Operation, "resourceVersion", req.Kind.Version)
+
+	// Validate resource type
+	if req.Resource.String() != workflowStepDefGVR.String() {
+		err := fmt.Errorf("expected resource to be %s, got %s", workflowStepDefGVR, req.Resource.String())
+		logger.WithStep("resource-check").WithError(err).Error(err, "Admission request targets unexpected resource type - rejecting request",
+			"expected", workflowStepDefGVR.String(),
+			"actual", req.Resource.String(),
+			"operation", req.Operation)
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("%s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	// Only validate create and update operations
+	if req.Operation != admissionv1.Create && req.Operation != admissionv1.Update {
+		logger.WithStep("skip-validation").Info("Skipping WorkflowStepDefinition validation - operation does not require validation", "operation", req.Operation, "reason", "only CREATE and UPDATE operations are validated")
+		return admission.ValidationResponse(true, "Operation does not require validation")
+	}
+
+	// Decode the object
+	obj := &v1beta1.WorkflowStepDefinition{}
+	if err := h.Decoder.Decode(req, obj); err != nil {
+		logger.WithStep("decode").WithError(err).Error(err, "Unable to decode admission request payload into WorkflowStepDefinition object - malformed request")
+		return admission.Errored(http.StatusBadRequest, fmt.Errorf("failed to decode: %s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	if obj.Spec.Version != "" {
+		logger = logger.WithValues("version", obj.Spec.Version)
+	}
+	logger.WithStep("decode").Info("Successfully decoded WorkflowStepDefinition from admission request",
+		"definitionName", obj.Name,
+		"namespace", obj.Namespace,
+		"hasSchematic", obj.Spec.Schematic != nil,
+		"version", obj.Spec.Version)
+
+	// Validate output resources
+	if obj.Spec.Schematic != nil && obj.Spec.Schematic.CUE != nil {
+		logger.WithStep("validate-output-resources").Info("Validating output resources referenced in WorkflowStepDefinition CUE template")
+		if err := webhookutils.ValidateOutputResourcesExist(obj.Spec.Schematic.CUE.Template, h.Client.RESTMapper(), obj); err != nil {
+			logger.WithStep("validate-output-resources").WithError(err).Error(err, "CUE template references output resources that don't exist in cluster - unknown resource types detected")
+			return admission.Denied(fmt.Sprintf("output resource validation failed: %s (requestUID=%s)", err.Error(), req.UID))
+		}
+		logger.WithStep("validate-output-resources").WithSuccess(true).Info("Output resources validation completed successfully - all referenced resources exist in cluster")
+	}
+
+	// Validate semantic version
+	if obj.Spec.Version != "" {
+		if err := webhookutils.ValidateSemanticVersion(obj.Spec.Version); err != nil {
+			logger.WithStep("validate-version").WithError(err).Error(err, "WorkflowStepDefinition version does not follow semantic versioning format (x.y.z)", "version", obj.Spec.Version, "expectedFormat", "x.y.z")
+			return admission.Denied(fmt.Sprintf("semantic version validation failed: %s (requestUID=%s)", err.Error(), req.UID))
+		}
+		logger.WithStep("validate-version").Info("WorkflowStepDefinition version follows semantic versioning format", "version", obj.Spec.Version)
+	}
+
+	// Validate version conflicts
+	revisionName := obj.Annotations[oam.AnnotationDefinitionRevisionName]
+	if err := webhookutils.ValidateMultipleDefVersionsNotPresent(obj.Spec.Version, revisionName, obj.Kind); err != nil {
+		logger.WithStep("validate-version-conflict").WithError(err).Error(err, "WorkflowStepDefinition has conflicting version specifications - cannot have both spec.version and revision annotation", "specVersion", obj.Spec.Version, "revisionName", revisionName)
+		return admission.Denied(fmt.Sprintf("definition version conflict: %s (requestUID=%s)", err.Error(), req.UID))
+	}
+
+	logger.WithStep("complete").WithSuccess(true, startTime).Info("WorkflowStepDefinition admission validation completed successfully - resource is valid and will be admitted", "definitionName", obj.Name, "operation", req.Operation)
+	return admission.ValidationResponse(true, "Validation passed")
+}
+
+// RegisterValidatingHandler registers the WorkflowStepDefinition validation webhook with the manager.
+func RegisterValidatingHandler(mgr manager.Manager) {
+	logger := logging.New()
+	logger.Info("Registering WorkflowStepDefinition validation webhook", "path", ValidationWebhookPath)
+
+	server := mgr.GetWebhookServer()
+	server.Register(ValidationWebhookPath, &webhook.Admission{
+		Handler: &ValidatingHandler{
+			Client:  mgr.GetClient(),
+			Decoder: admission.NewDecoder(mgr.GetScheme()),
+		},
+	})
+}
diff --git a/pkg/webhook/utils/invalid_resource_check.go b/pkg/webhook/utils/invalid_resource_check.go
new file mode 100644
index 00000000..2f2d336c
--- /dev/null
+++ b/pkg/webhook/utils/invalid_resource_check.go
@@ -0,0 +1,257 @@
+/*
+ Copyright 2021. The KubeVela 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 utils
+
+import (
+	"fmt"
+	"strings"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/cue/token"
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/oam-dev/kubevela/pkg/features"
+)
+
+// ExtractResourceInfo extracts apiVersion and kind from CUE template without evaluation
+func ExtractResourceInfo(cueTemplate string) ([]ResourceInfo, error) {
+	file, err := parser.ParseFile("", cueTemplate, parser.ParseComments)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse CUE template: %w", err)
+	}
+
+	var resources []ResourceInfo
+
+	// Walk through the AST to find output and outputs fields
+	ast.Walk(file, func(node ast.Node) bool {
+		if n, ok := node.(*ast.Field); ok {
+			label := extractLabel(n.Label)
+			if label == "output" || label == "outputs" {
+				if label == "output" {
+					// Extract from single output field
+					if resource := extractResourceFromStruct(n.Value); resource != nil {
+						resources = append(resources, *resource)
+					}
+				} else if label == "outputs" {
+					// Extract from outputs field (multiple resources)
+					resources = append(resources, extractResourcesFromOutputs(n.Value)...)
+				}
+			}
+		}
+		return true
+	}, nil)
+
+	return resources, nil
+}
+
+type ResourceInfo struct {
+	APIVersion string
+	Kind       string
+	Name       string // optional, for better error messages
+}
+
+func extractLabel(label ast.Label) string {
+	switch l := label.(type) {
+	case *ast.Ident:
+		return l.Name
+	case *ast.BasicLit:
+		if l.Kind == token.STRING {
+			// Remove quotes
+			return strings.Trim(l.Value, `"`)
+		}
+	}
+	return ""
+}
+
+func extractResourceFromStruct(expr ast.Expr) *ResourceInfo {
+	structLit, ok := expr.(*ast.StructLit)
+	if !ok {
+		return nil
+	}
+
+	resource := &ResourceInfo{}
+
+	for _, elt := range structLit.Elts {
+		field, ok := elt.(*ast.Field)
+		if !ok {
+			continue
+		}
+
+		label := extractLabel(field.Label)
+		value := extractStringValue(field.Value)
+
+		switch label {
+		case "apiVersion":
+			resource.APIVersion = value
+		case "kind":
+			resource.Kind = value
+		case "metadata":
+			// Try to extract name from metadata.name
+			if name := extractNameFromMetadata(field.Value); name != "" {
+				resource.Name = name
+			}
+		}
+	}
+
+	// Only return if we have both apiVersion and kind
+	if resource.APIVersion != "" && resource.Kind != "" {
+		return resource
+	}
+	return nil
+}
+
+func extractResourcesFromOutputs(expr ast.Expr) []ResourceInfo {
+	var resources []ResourceInfo
+
+	structLit, ok := expr.(*ast.StructLit)
+	if !ok {
+		return resources
+	}
+
+	for _, elt := range structLit.Elts {
+		field, ok := elt.(*ast.Field)
+		if !ok {
+			continue
+		}
+
+		if resource := extractResourceFromStruct(field.Value); resource != nil {
+			// Use the field label as the resource name if not found in metadata
+			if resource.Name == "" {
+				resource.Name = extractLabel(field.Label)
+			}
+			resources = append(resources, *resource)
+		}
+	}
+
+	return resources
+}
+
+func extractStringValue(expr ast.Expr) string {
+	if e, ok := expr.(*ast.BasicLit); ok {
+		if e.Kind == token.STRING {
+			return strings.Trim(e.Value, `"`)
+		}
+	}
+	return ""
+}
+
+func extractNameFromMetadata(expr ast.Expr) string {
+	structLit, ok := expr.(*ast.StructLit)
+	if !ok {
+		return ""
+	}
+
+	for _, elt := range structLit.Elts {
+		field, ok := elt.(*ast.Field)
+		if !ok {
+			continue
+		}
+
+		if extractLabel(field.Label) == "name" {
+			return extractStringValue(field.Value)
+		}
+	}
+	return ""
+}
+
+// ValidateOutputResourcesExist validates that resources referenced in output/outputs fields exist on the cluster
+func ValidateOutputResourcesExist(cueTemplate string, mapper meta.RESTMapper, obj client.Object) error {
+	// Check if feature gate is enabled FIRST before doing anything
+	if !utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateResourcesExist) {
+		return nil // Skip validation if feature is disabled
+	}
+
+	// Skip validation for addon definitions
+	// Addons often bundle CRDs with definitions that reference them
+	if IsAddonDefinition(obj) {
+		return nil
+	}
+
+	// Only extract and validate resources if feature is enabled and not an addon
+	resources, err := ExtractResourceInfo(cueTemplate)
+	if err != nil {
+		return fmt.Errorf("failed to extract resource info: %w", err)
+	}
+
+	for _, resource := range resources {
+		gvk := schema.GroupVersionKind{
+			Version: resource.APIVersion,
+			Kind:    resource.Kind,
+		}
+
+		// Parse the apiVersion to get group and version
+		if strings.Contains(resource.APIVersion, "/") {
+			parts := strings.SplitN(resource.APIVersion, "/", 2)
+			gvk.Group = parts[0]
+			gvk.Version = parts[1]
+		} else {
+			// Core API resources (like v1) don't have a group
+			gvk.Group = ""
+			gvk.Version = resource.APIVersion
+		}
+
+		_, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
+		if err != nil {
+			ref := fmt.Sprintf("%s/%s", resource.APIVersion, resource.Kind)
+			return fmt.Errorf("resource type not found on cluster: %s (%w)", ref, err)
+		}
+	}
+
+	return nil
+}
+
+// IsAddonDefinition checks if the object is part of an addon installation
+// This is a generic solution that works for ALL addons by checking owner references
+func IsAddonDefinition(obj client.Object) bool {
+	if obj == nil {
+		return false
+	}
+
+	// Generic approach: Check if the object has an owner reference to an addon application
+	// All addon definitions get owner references to their addon application (pattern: addon-{name})
+	for _, ownerRef := range obj.GetOwnerReferences() {
+		if ownerRef.Kind == "Application" &&
+			ownerRef.APIVersion == "core.oam.dev/v1beta1" &&
+			strings.HasPrefix(ownerRef.Name, "addon-") {
+			return true
+		}
+	}
+
+	// Fallback checks for edge cases where owner references might not be set yet
+	labels := obj.GetLabels()
+	if labels != nil {
+		// Check if this is managed by an addon application
+		appName, hasApp := labels["app.oam.dev/name"]
+		if hasApp && strings.HasPrefix(appName, "addon-") {
+			return true
+		}
+	}
+
+	// Also check annotations for addon markers
+	annotations := obj.GetAnnotations()
+	if annotations != nil {
+		if addonName, hasAddon := annotations["addons.oam.dev/name"]; hasAddon && addonName != "" {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/references/appfile/addon.go b/references/appfile/addon.go
index 5e88da79..448ba298 100644
--- a/references/appfile/addon.go
+++ b/references/appfile/addon.go
@@ -95,11 +95,7 @@ func ApplyTerraform(app *v1beta1.Application, k8sClient client.Client, ioStream
 				return nil, err
 			}
 
-			outputList := strings.Split(strings.ReplaceAll(string(outputs), " ", ""), "\n")
-			if outputList[len(outputList)-1] == "" {
-				outputList = outputList[:len(outputList)-1]
-			}
-			if err := generateSecretFromTerraformOutput(k8sClient, outputList, name, namespace); err != nil {
+			if err := generateSecretFromTerraformOutput(k8sClient, string(outputs), name, namespace); err != nil {
 				return nil, err
 			}
 		default:
@@ -135,21 +131,32 @@ func callTerraform(tfJSONDir string) ([]byte, error) {
 }
 
 // generateSecretFromTerraformOutput generates secret from Terraform output
-func generateSecretFromTerraformOutput(k8sClient client.Client, outputList []string, name, namespace string) error {
+func generateSecretFromTerraformOutput(k8sClient client.Client, rawOutput string, name, namespace string) error {
 	ctx := context.TODO()
-	err := k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}})
-	if err == nil {
-		return fmt.Errorf("namespace %s doesn't exist", namespace)
+
+	// Check if namespace exists
+	var ns v1.Namespace
+	if err := k8sClient.Get(ctx, client.ObjectKey{Name: namespace}, &ns); err != nil {
+		if errors.IsNotFound(err) {
+			return fmt.Errorf("namespace %s doesn't exist", namespace)
+		}
+		return fmt.Errorf("failed to get namespace %s: %w", namespace, err)
 	}
-	var cmData = make(map[string]string, len(outputList))
+
+	var cmData = make(map[string]string)
+	outputList := strings.Split(rawOutput, "\n")
 	for _, i := range outputList {
-		line := strings.Split(i, "=")
+		if strings.TrimSpace(i) == "" {
+			continue
+		}
+		line := strings.SplitN(i, "=", 2)
 		if len(line) != 2 {
-			return fmt.Errorf("terraform output isn't in the right format")
+			return fmt.Errorf("terraform output isn't in the right format: %q", i)
 		}
 		k := strings.TrimSpace(line[0])
-		v := strings.TrimSpace(line[1])
-		if k != "" && v != "" {
+		// Terraform string outputs are quoted, remove them.
+		v := strings.Trim(strings.TrimSpace(line[1]), "\"")
+		if k != "" {
 			cmData[k] = v
 		}
 	}
diff --git a/references/cli/dryrun.go b/references/cli/dryrun.go
index 169f618d..145022ce 100644
--- a/references/cli/dryrun.go
+++ b/references/cli/dryrun.go
@@ -183,7 +183,8 @@ func DryRunApplication(cmdOption *DryRunCmdOptions, c common.Args, namespace str
 	}
 
 	if app.Namespace != "" && namespace != "" && app.Namespace != namespace {
-		return buff, errors.WithMessage(fmt.Errorf("error: conflicting namespace found in file and flag %s doesn't match with namespace %s ", namespace, app.Namespace), "The namespace must be unique")
+		// Overwrite application namespace with flag-provided namespace (change replicated from upstream PR #6855)
+		app.Namespace = namespace
 	}
 
 	switch {
diff --git a/references/cli/test-data/dry-run/testing-dry-run-override.yaml b/references/cli/test-data/dry-run/testing-dry-run-override.yaml
new file mode 100644
index 00000000..f7676e06
--- /dev/null
+++ b/references/cli/test-data/dry-run/testing-dry-run-override.yaml
@@ -0,0 +1,11 @@
+apiVersion: core.oam.dev/v1beta1
+kind: Application
+metadata:
+  name: testing-app-override
+  namespace: original-ns
+spec:
+  components:
+    - name: testing-dryrun
+      type: webservice
+      properties:
+        image: oamdev/hello-world:v1
diff --git a/vela-templates/definitions/internal/workflowstep/vela-cli.cue b/vela-templates/definitions/internal/workflowstep/vela-cli.cue
index 0af351f6..c43f3bd5 100644
--- a/vela-templates/definitions/internal/workflowstep/vela-cli.cue
+++ b/vela-templates/definitions/internal/workflowstep/vela-cli.cue
@@ -133,8 +133,6 @@ template: {
 	}
 
 	parameter: {
-		// +usage=Specify the name of the addon.
-		addonName: string
 		// +usage=Specify the vela command
 		command: [...string]
 		// +usage=Specify the image



Test patch:
diff --git a/.github/actions/e2e-test/README.md b/.github/actions/e2e-test/README.md
new file mode 100644
index 00000000..ef5ca8b9
--- /dev/null
+++ b/.github/actions/e2e-test/README.md
@@ -0,0 +1,51 @@
+# Kubevela K8s Upgrade E2E Test Action
+
+A comprehensive GitHub composite action for running KubeVela Kubernetes upgrade end-to-end (E2E) tests with complete environment setup, multiple test suites, and failure diagnostics.
+
+
+> **Note**: This action requires the `GO_VERSION` environment variable to be set in your workflow.
+
+## Quick Start
+
+### Basic Usage
+
+```yaml
+name: E2E Tests
+on: [push, pull_request]
+
+jobs:
+  e2e-tests:
+    runs-on: ubuntu-latest
+    env:
+      GO_VERSION: '1.23.8'
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+        
+      - name: Run KubeVela E2E Tests
+        uses: ./.github/actions/upgrade-e2e-test
+```
+
+## Test Flow Diagram
+
+```
+┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
+│ Environment     │    │ E2E Environment  │    │ Test Execution  │
+│ Setup           │───▶│ Preparation      │───▶│ (3 Suites)      │
+│                 │    │                  │    │                 │
+│ • Install tools │    │ • Cleanup        │    │ • API tests     │
+│ • Setup Go      │    │ • Core setup     │    │ • Addon tests   │
+│ • Dependencies  │    │ • Helm tests     │    │ • General tests │
+│ • Build project │    │                  │    │                 │
+└─────────────────┘    └──────────────────┘    └─────────────────┘
+                                                        │
+                                                        ▼
+                                                ┌─────────────────┐
+                                                │ Diagnostics     │
+                                                │ (On Failure)    │
+                                                │                 │
+                                                │ • Cluster logs  │
+                                                │ • System events │
+                                                │ • Test artifacts│
+                                                └─────────────────┘
+```
\ No newline at end of file
diff --git a/.github/actions/e2e-test/action.yaml b/.github/actions/e2e-test/action.yaml
new file mode 100644
index 00000000..068d6959
--- /dev/null
+++ b/.github/actions/e2e-test/action.yaml
@@ -0,0 +1,96 @@
+name: 'Kubevela K8s Upgrade e2e Test'
+description: 'Runs Kubevela K8s upgrade e2e tests, uploads coverage, and collects diagnostics on failure.'
+
+inputs:
+  codecov-token:
+    description: 'Codecov token for uploading coverage reports'
+    required: false
+    default: ''
+  codecov-enable:
+    description: 'Enable codecov coverage upload'
+    required: false
+    default: 'false'
+
+runs:
+  using: "composite"
+  steps:
+    # ========================================================================
+    # Environment Setup
+    # ========================================================================
+    - name: Configure environment setup
+      uses: ./.github/actions/env-setup
+
+    - name: Build project
+      shell: bash
+      run: make
+
+    # ========================================================================
+    # E2E Test Environment Preparation
+    # ========================================================================
+    - name: Prepare e2e environment
+      shell: bash
+      run: |
+        echo "Preparing e2e test environment..."
+        make e2e-cleanup
+        make e2e-setup-core
+
+        echo "Running Helm tests..."
+        helm test -n vela-system kubevela --timeout 5m
+
+    # ========================================================================
+    # E2E Test Execution
+    # ========================================================================
+    - name: Run API e2e tests
+      shell: bash
+      run: |
+        echo "Running API e2e tests..."
+        make e2e-api-test
+
+    - name: Run addon e2e tests
+      shell: bash
+      run: |
+        echo "Running addon e2e tests..."
+        make e2e-addon-test
+
+    - name: Run general e2e tests
+      shell: bash
+      run: |
+        echo "Running general e2e tests..."
+        make e2e-test
+
+    - name: Upload coverage report
+      if: ${{ inputs.codecov-enable == 'true' }}
+      uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+      with:
+        token: ${{ inputs.codecov-token }}
+        files: ./coverage.txt
+        flags: core-unittests
+        name: codecov-umbrella
+        fail_ci_if_error: false
+
+    # ========================================================================
+    # Failure Diagnostics
+    # ========================================================================
+    - name: Collect failure diagnostics
+      if: failure()
+      shell: bash
+      run: |
+        echo "=== FAILURE DIAGNOSTICS ==="
+        echo "Collecting diagnostic information for debugging..."
+
+        echo "--- Cluster Status ---"
+        kubectl get nodes -o wide || true
+        kubectl get pods -A || true
+
+        echo "--- KubeVela System Logs ---"
+        kubectl logs -n vela-system -l app.kubernetes.io/name=vela-core --tail=100 || true
+
+        echo "--- Recent Events ---"
+        kubectl get events -A --sort-by='.lastTimestamp' --field-selector type!=Normal || true
+
+        echo "--- Helm Release Status ---"
+        helm list -A || true
+        helm status kubevela -n vela-system || true
+
+        echo "--- Test Artifacts ---"
+        find . -name "*.log" -type f -exec echo "=== {} ===" \; -exec cat {} \; || true
\ No newline at end of file
diff --git a/.github/actions/multicluster-test/README.md b/.github/actions/multicluster-test/README.md
new file mode 100644
index 00000000..45c69ef5
--- /dev/null
+++ b/.github/actions/multicluster-test/README.md
@@ -0,0 +1,35 @@
+# Kubevela K8s Upgrade Multicluster E2E Test Action
+
+A comprehensive GitHub Actions composite action for running Kubevela Kubernetes upgrade multicluster end-to-end tests with automated coverage reporting and failure diagnostics.
+
+## Usage
+
+
+```yaml
+name: Kubevela Multicluster E2E Tests
+on:
+  push:
+    branches: [main, develop]
+  pull_request:
+    branches: [main]
+
+jobs:
+  multicluster-e2e:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Run Multicluster E2E Tests
+        uses: ./.github/actions/multicluster-test
+        with:
+          codecov-enable: 'true'
+          codecov-token: ${{ secrets.CODECOV_TOKEN }}
+```
+
+## Inputs
+
+| Input | Description | Required | Default | Type |
+|-------|-------------|----------|---------|------|
+| `codecov-token` | Codecov token for uploading coverage reports | No | `''` | string |
+| `codecov-enable` | Enable codecov coverage upload | No | `'false'` | string |
diff --git a/.github/actions/multicluster-test/action.yaml b/.github/actions/multicluster-test/action.yaml
new file mode 100644
index 00000000..61e5882c
--- /dev/null
+++ b/.github/actions/multicluster-test/action.yaml
@@ -0,0 +1,76 @@
+name: 'Kubevela K8s Upgrade Multicluster E2E Test'
+description: 'Runs Kubevela Kubernetes upgrade multicluster end-to-end tests, uploads coverage, and collects diagnostics on failure.'
+author: 'viskumar_gwre'
+
+inputs:
+  codecov-token:
+    description: 'Codecov token for uploading coverage reports'
+    required: false
+    default: ''
+  codecov-enable:
+    description: 'Enable codecov coverage upload'
+    required: false
+    default: 'false'
+
+runs:
+  using: 'composite'
+  steps:
+    # ========================================================================
+    # Environment Setup
+    # ========================================================================
+    - name: Configure environment setup
+      uses: ./.github/actions/env-setup
+
+    # ========================================================================
+    # E2E Test Execution
+    # ========================================================================
+    - name: Prepare e2e test environment
+      shell: bash
+      run: |
+        # Build CLI tools and prepare test environment
+        echo "Building KubeVela CLI..."
+        make vela-cli
+
+        echo "Cleaning up previous test artifacts..."
+        make e2e-cleanup
+
+        echo "Setting up core authentication for e2e tests..."
+        make e2e-setup-core-auth
+
+    - name: Execute multicluster upgrade e2e tests
+      shell: bash
+      run: |
+        # Add built CLI to PATH and run multicluster tests
+        export PATH=$(pwd)/bin:$PATH
+
+        echo "Running e2e multicluster upgrade tests..."
+        make e2e-multicluster-test
+
+    - name: Upload coverage report
+      if: ${{ inputs.codecov-enable == 'true' }}
+      uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+      with:
+        token: ${{ inputs.codecov-token }}
+        files: /tmp/e2e-profile.out,/tmp/e2e_multicluster_test.out
+        flags: e2e-multicluster-test
+        name: codecov-umbrella
+
+    # ========================================================================
+    # Failure Diagnostics
+    # ========================================================================
+    - name: Collect failure diagnostics
+      if: failure()
+      shell: bash
+      run: |
+        echo "=== FAILURE DIAGNOSTICS ==="
+        echo "Collecting diagnostic information for debugging..."
+
+        echo "--- Cluster Status ---"
+        kubectl get nodes -o wide || true
+        kubectl get pods -A || true
+
+        echo "--- KubeVela System Logs ---"
+        kubectl logs -n vela-system -l app.kubernetes.io/name=vela-core --tail=100 || true
+
+        echo "--- Recent Events ---"
+        kubectl get events -A --sort-by='.lastTimestamp' --field-selector type!=Normal || true
\ No newline at end of file
diff --git a/.github/actions/unit-test/README.md b/.github/actions/unit-test/README.md
new file mode 100644
index 00000000..492389bc
--- /dev/null
+++ b/.github/actions/unit-test/README.md
@@ -0,0 +1,34 @@
+# Kubevela K8s Upgrade Unit Test Action
+
+A comprehensive GitHub composite action for running KubeVela Kubernetes upgrade unit tests with coverage reporting and failure diagnostics.
+
+## Inputs
+
+| Input | Description | Required | Default |
+|-------|-------------|----------|---------|
+| `codecov-token` | Codecov token for uploading coverage reports | ❌ | `''` |
+| `codecov-enable` | Enable Codecov coverage upload (`'true'` or `'false'`) | ❌ | `'false'` |
+| `go-version` | Go version to use for testing | ❌ | `'1.23.8'` |
+
+## Quick Start
+
+### Basic Usage
+
+```yaml
+name: Unit Tests with Coverage
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+        
+      - name: Run KubeVela Unit Tests
+        uses: viskumar_gwre/kubevela-k8s-upgrade-unit-test-action@v1
+        with:
+          codecov-enable: 'true'
+          codecov-token: ${{ secrets.CODECOV_TOKEN }}
+          go-version: '1.23.8'
+```
\ No newline at end of file
diff --git a/.github/actions/unit-test/action.yaml b/.github/actions/unit-test/action.yaml
new file mode 100644
index 00000000..e5cf2e19
--- /dev/null
+++ b/.github/actions/unit-test/action.yaml
@@ -0,0 +1,67 @@
+name: 'Kubevela K8s Upgrade Unit Test'
+description: 'Runs Kubevela K8s upgrade unit tests, uploads coverage, and collects diagnostics on failure.'
+
+inputs:
+  codecov-token:
+    description: 'Codecov token for uploading coverage reports'
+    required: false
+    default: ''
+  codecov-enable:
+    description: 'Enable codecov coverage upload'
+    required: false
+    default: 'false'
+
+runs:
+  using: "composite"
+  steps:
+    # ========================================================================
+    # Environment Setup
+    # ========================================================================
+    - name: Configure environment setup
+      uses: ./.github/actions/env-setup
+
+    # ========================================================================
+    # Unit Test Execution
+    # ========================================================================
+    - name: Run unit tests
+      shell: bash
+      run: |
+        echo "Running unit tests..."
+        make test
+
+    - name: Upload coverage report
+      if: ${{ inputs.codecov-enable == 'true' }}
+      uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+      with:
+        token: ${{ inputs.codecov-token }}
+        files: ./coverage.txt
+        flags: core-unittests
+        name: codecov-umbrella
+        fail_ci_if_error: false
+
+    # ========================================================================
+    # Failure Diagnostics
+    # ========================================================================
+    - name: Collect failure diagnostics
+      if: failure()
+      shell: bash
+      run: |
+        echo "=== FAILURE DIAGNOSTICS ==="
+        echo "Collecting diagnostic information for debugging..."
+
+        echo "--- Go Environment ---"
+        go version || true
+        go env || true
+
+        echo "--- Cluster Status ---"
+        kubectl get nodes -o wide || true
+        kubectl get pods -A || true
+
+        echo "--- KubeVela System Logs ---"
+        kubectl logs -n vela-system -l app.kubernetes.io/name=vela-core --tail=100 || true
+
+        echo "--- Recent Events ---"
+        kubectl get events -A --sort-by='.lastTimestamp' --field-selector type!=Normal || true
+
+        echo "--- Test Artifacts ---"
+        find . -name "*.log" -o -name "*test*.xml" -o -name "coverage.*" | head -20 || true
\ No newline at end of file
diff --git a/e2e/plugin/plugin_test.go b/e2e/plugin/plugin_test.go
index 8efe5871..06c033c3 100644
--- a/e2e/plugin/plugin_test.go
+++ b/e2e/plugin/plugin_test.go
@@ -425,7 +425,7 @@ spec:
         }
         
         outputs: ingress: {
-        	apiVersion: "networking.k8s.io/v1beta1"
+        	apiVersion: "networking.k8s.io/v1"
         	kind:       "Ingress"
         	metadata:
         		name: context.name
@@ -436,9 +436,14 @@ spec:
         				paths: [
         					for k, v in parameter.http {
         						path: k
+        						pathType: "Prefix"
         						backend: {
-        							serviceName: context.name
-        							servicePort: v
+        							service: {
+        								name: context.name
+        								port: {
+        									number: v
+        								}
+        							}
         						}
         					},
         				]
@@ -617,7 +622,7 @@ spec:
 
 ---
 ## From the trait test-ingress 
-apiVersion: networking.k8s.io/v1beta1
+apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
   annotations: {}
@@ -637,9 +642,12 @@ spec:
     http:
       paths:
       - backend:
-          serviceName: express-server
-          servicePort: 80
+          service:
+            name: express-server
+            port:
+              number: 80
         path: /
+        pathType: Prefix
 
 ---
 `
@@ -721,7 +729,7 @@ var livediffResult = `Application (test-vela-app) has been modified(*)
 -     app.oam.dev/component: express-server
   
 * Component (express-server) / Trait (test-ingress/ingress) has been removed(-)
-- apiVersion: networking.k8s.io/v1beta1
+- apiVersion: networking.k8s.io/v1
 - kind: Ingress
 - metadata:
 -   labels:
@@ -739,9 +747,12 @@ var livediffResult = `Application (test-vela-app) has been modified(*)
 -     http:
 -       paths:
 -       - backend:
--           serviceName: express-server
--           servicePort: 80
+-           service:
+-             name: express-server
+-             port:
+-               number: 80
 -         path: /
+-         pathType: Prefix
   
 * Component (new-express-server) has been added(+)
 + apiVersion: apps/v1
@@ -796,7 +807,7 @@ var livediffResult = `Application (test-vela-app) has been modified(*)
 +     app.oam.dev/component: new-express-server
   
 * Component (new-express-server) / Trait (test-ingress/ingress) has been added(+)
-+ apiVersion: networking.k8s.io/v1beta1
++ apiVersion: networking.k8s.io/v1
 + kind: Ingress
 + metadata:
 +   labels:
@@ -814,9 +825,12 @@ var livediffResult = `Application (test-vela-app) has been modified(*)
 +     http:
 +       paths:
 +       - backend:
-+           serviceName: new-express-server
-+           servicePort: 8080
++           service:
++             name: new-express-server
++             port:
++               number: 8080
 +         path: /
++         pathType: Prefix
 `
 
 var testShowComponentDef = `
diff --git a/pkg/addon/helper_test.go b/pkg/addon/helper_test.go
index 15c6dbcf..0d3dd0de 100644
--- a/pkg/addon/helper_test.go
+++ b/pkg/addon/helper_test.go
@@ -39,7 +39,7 @@ func setupMockServer() *httptest.Server {
 			"index.yaml",
 			"fluxcd-test-version-1.0.0.tgz",
 			"fluxcd-test-version-2.0.0.tgz",
-			"vela-workflow-v0.3.5.tgz",
+			"vela-workflow-v0.6.2.tgz",
 			"foo-v1.0.0.tgz",
 			"bar-v1.0.0.tgz",
 			"bar-v2.0.0.tgz",
diff --git a/pkg/addon/reader_gitlab_test.go b/pkg/addon/reader_gitlab_test.go
index e193767a..e56e490e 100644
--- a/pkg/addon/reader_gitlab_test.go
+++ b/pkg/addon/reader_gitlab_test.go
@@ -111,3 +111,39 @@ func TestGitlabReader(t *testing.T) {
 	_, err := r.ReadFile("example/metadata.yaml")
 	assert.NoError(t, err)
 }
+
+func TestGitLabItem(t *testing.T) {
+	t.Run("Getters", func(t *testing.T) {
+		item := GitLabItem{
+			tp:   "blob",
+			name: "metadata.yaml",
+		}
+		assert.Equal(t, "blob", item.GetType())
+		assert.Equal(t, "metadata.yaml", item.GetName())
+	})
+
+	t.Run("GetPath", func(t *testing.T) {
+		testCases := map[string]struct {
+			basePath string
+			fullPath string
+			expected string
+		}{
+			"no base path": {
+				basePath: "",
+				fullPath: "fluxcd/metadata.yaml",
+				expected: "fluxcd/metadata.yaml",
+			},
+			"with base path": {
+				basePath: "addons",
+				fullPath: "addons/fluxcd/metadata.yaml",
+				expected: "fluxcd/metadata.yaml",
+			},
+		}
+		for name, tc := range testCases {
+			t.Run(name, func(t *testing.T) {
+				item := GitLabItem{basePath: tc.basePath, path: tc.fullPath}
+				assert.Equal(t, tc.expected, item.GetPath())
+			})
+		}
+	})
+}
diff --git a/pkg/appfile/appfile_test.go b/pkg/appfile/appfile_test.go
index 521a5aa9..cca5e852 100644
--- a/pkg/appfile/appfile_test.go
+++ b/pkg/appfile/appfile_test.go
@@ -19,26 +19,33 @@ package appfile
 import (
 	"context"
 	"encoding/json"
+	"strings"
 	"testing"
 
 	"cuelang.org/go/cue/cuecontext"
+	"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
 	"github.com/crossplane/crossplane-runtime/pkg/test"
 	"github.com/google/go-cmp/cmp"
 	terraformtypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
 	terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 	appsv1 "k8s.io/api/apps/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"sigs.k8s.io/yaml"
 
 	"github.com/kubevela/workflow/pkg/cue/model"
 
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	oamtypes "github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/cue/definition"
@@ -47,8 +54,9 @@ import (
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 )
 
-var _ = Describe("Test Workflow", func() {
-	It("generate workflow task runners", func() {
+func TestWorkflow(t *testing.T) {
+	k8sClient := fake.NewClientBuilder().Build()
+	t.Run("generate workflow task runners", func(t *testing.T) {
 		workflowStepDef := v1beta1.WorkflowStepDefinition{
 			Spec: v1beta1.WorkflowStepDefinitionSpec{
 				Schematic: &common.Schematic{
@@ -65,7 +73,7 @@ wait: op.#ConditionalWait & {
 		workflowStepDef.Name = "test-wait"
 		workflowStepDef.Namespace = "default"
 		err := k8sClient.Create(context.Background(), &workflowStepDef)
-		Expect(err).To(BeNil())
+		assert.NoError(t, err)
 
 		notCueStepDef := v1beta1.WorkflowStepDefinition{
 			Spec: v1beta1.WorkflowStepDefinitionSpec{
@@ -76,12 +84,12 @@ wait: op.#ConditionalWait & {
 		notCueStepDef.Name = "not-cue"
 		notCueStepDef.Namespace = "default"
 		err = k8sClient.Create(context.Background(), &notCueStepDef)
-		Expect(err).To(BeNil())
+		assert.NoError(t, err)
 	})
-})
+}
 
-var _ = Describe("Test Terraform schematic appfile", func() {
-	It("workload capability is Terraform", func() {
+func TestTerraformSchematicAppfile(t *testing.T) {
+	t.Run("workload capability is Terraform", func(t *testing.T) {
 		var (
 			ns            = "default"
 			compName      = "sample-db"
@@ -202,10 +210,10 @@ variable "password" {
 
 		comps, err := af.GenerateComponentManifests()
 		diff := cmp.Diff(comps[0], expectCompManifest)
-		Expect(diff).ShouldNot(BeEmpty())
-		Expect(err).Should(BeNil())
+		assert.NotEmpty(t, diff)
+		assert.NoError(t, err)
 	})
-})
+}
 
 func TestSetParameterValuesToKubeObj(t *testing.T) {
 	tests := map[string]struct {
@@ -301,18 +309,20 @@ func TestSetParameterValuesToKubeObj(t *testing.T) {
 			obj := tc.obj.DeepCopy()
 			err := setParameterValuesToKubeObj(obj, tc.values)
 			if diff := cmp.Diff(tc.wantObj, *obj); diff != "" {
-				t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
+				t.Errorf(
+					"\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
 			}
 			if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
-				t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
+				t.Errorf(
+					"\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
 			}
 		})
 	}
 
 }
 
-var _ = Describe("Test evalWorkloadWithContext", func() {
-	It("workload capability is Terraform", func() {
+func TestEvalWorkloadWithContext(t *testing.T) {
+	t.Run("workload capability is Terraform", func(t *testing.T) {
 		var (
 			ns       = "default"
 			compName = "sample-db"
@@ -404,11 +414,11 @@ variable "password" {
 		}, args.wl.Name)
 		pCtx := NewBasicContext(ctxData, args.wl.Params)
 		comp, err := evalWorkloadWithContext(pCtx, args.wl, ns, args.appName)
-		Expect(comp.ComponentOutput).ShouldNot(BeNil())
-		Expect(comp.Name).Should(Equal(""))
-		Expect(err).Should(BeNil())
+		assert.NotNil(t, comp.ComponentOutput)
+		assert.Equal(t, "", comp.Name)
+		assert.NoError(t, err)
 	})
-})
+}
 
 func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
 	var (
@@ -700,16 +710,16 @@ func TestBaseGenerateComponent(t *testing.T) {
 	}, wlName)
 	pContext := NewBasicContext(ctxData, nil)
 	base := `
-	apiVersion: "apps/v1"
-	kind:       "Deployment"
-	spec: {
-		template: {
-			spec: containers: [{
-				image: "nginx"
-			}]
+		apiVersion: "apps/v1"
+		kind:       "Deployment"
+		spec: {
+			template: {
+				spec: containers: [{
+					image: "nginx"
+				}]
+			}
 		}
-	}
-`
+	`
 	inst := cuecontext.New().CompileString(base)
 	bs, _ := model.NewBase(inst.Value())
 	err := pContext.SetBase(bs)
@@ -738,8 +748,8 @@ if context.componentType == "stateless" {
 	assert.Equal(t, cm.ComponentOutputsAndTraits[0].Object["publishVersion"], publishVersion)
 }
 
-var _ = Describe("Test use context.appLabels& context.appAnnotations in componentDefinition ", func() {
-	It("Test generate AppConfig resources from ", func() {
+func TestAppLabelsAndAnnotationsInComponentDefinition(t *testing.T) {
+	t.Run("generate AppConfig resources", func(t *testing.T) {
 		af := &Appfile{
 			Name:      "app",
 			Namespace: "ns",
@@ -769,7 +779,6 @@ var _ = Describe("Test use context.appLabels& context.appAnnotations in componen
 								selector: matchLabels: {
 									"app.oam.dev/component": context.name
 								}
-						  
 								template: {
 									metadata: {
 										labels: {
@@ -783,47 +792,971 @@ var _ = Describe("Test use context.appLabels& context.appAnnotations in componen
 											}
 										}
 									}
-						  
 									spec: {
-										containers: [{
+										containers: [{ 
 											name:  context.name
 											image: parameter.image
-						  
 											if parameter["cmd"] != _|_ {
 												command: parameter.cmd
 											}
 										}]
 									}
 								}
-						  
 								selector:
 									matchLabels:
 										"app.oam.dev/component": context.name
 							}
 						  }
-						  
-						  parameter: {
+						  parameter: { 
 							// +usage=Which image would you like to use for your service
 							// +short=i
 							image: string
-						  
 							cmd?: [...string]
 						  }`},
 				},
 			},
 		}
-		By("Generate ComponentManifests")
+
+		// Generate ComponentManifests
 		componentManifests, err := af.GenerateComponentManifests()
-		Expect(err).To(BeNil())
-		By("Verify expected ComponentManifest")
+		if err != nil {
+			t.Fatalf("GenerateComponentManifests() error = %v", err)
+		}
+
+		// Verify expected ComponentManifest
 		deployment := &appsv1.Deployment{}
-		runtime.DefaultUnstructuredConverter.FromUnstructured(componentManifests[0].ComponentOutput.Object, deployment)
+		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(
+			componentManifests[0].ComponentOutput.Object, deployment); err != nil {
+			t.Fatalf("Failed to convert to Deployment: %v", err)
+		}
+
 		labels := deployment.Spec.Template.Labels
 		annotations := deployment.Spec.Template.Annotations
-		Expect(cmp.Diff(len(labels), 2)).Should(BeEmpty())
-		Expect(cmp.Diff(len(annotations), 2)).Should(BeEmpty())
-		Expect(cmp.Diff(labels["lk1"], "lv1")).Should(BeEmpty())
-		Expect(cmp.Diff(annotations["ak1"], "av1")).Should(BeEmpty())
+
+		if diff := cmp.Diff(len(labels), 2); diff != "" {
+			t.Errorf("labels length mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(len(annotations), 2); diff != "" {
+			t.Errorf("annotations length mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(labels["lk1"], "lv1"); diff != "" {
+			t.Errorf("label mismatch (-want +got): %s", diff)
+		}
+		if diff := cmp.Diff(annotations["ak1"], "av1"); diff != "" {
+			t.Errorf("annotation mismatch (-want +got): %s", diff)
+		}
+	})
+}
+
+func TestIsNotFoundInAppFile(t *testing.T) {
+	tests := map[string]struct {
+		err  error
+		want bool
+	}{
+		"ErrorIsNil": {
+			err:  nil,
+			want: false,
+		},
+		"ErrorContainsSubstring": {
+			err:  errors.New("some error not found in appfile"),
+			want: true,
+		},
+		"ErrorNotContainsSubstring": {
+			err:  errors.New("some other error"),
+			want: false,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			got := IsNotFoundInAppFile(tc.err)
+			assert.Equal(t, tc.want, got)
+		})
+	}
+}
+
+func TestPrepareProcessContext(t *testing.T) {
+	ctxData := process.ContextData{
+		AppName:   "test-app",
+		Namespace: "test-ns",
+	}
+
+	t.Run("Success case with nil initial context", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+		assert.Nil(t, comp.Ctx)
+
+		ctx, err := PrepareProcessContext(comp, ctxData)
+
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx)
+		assert.NotNil(t, comp.Ctx)
+		assert.Equal(t, ctx, comp.Ctx)
+	})
+
+	t.Run("Error case from invalid CUE template", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string & int}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+
+		_, err := PrepareProcessContext(comp, ctxData)
+
+		assert.Error(t, err)
+		assert.ErrorContains(t, err, "conflicting values string and int")
 	})
 
-})
+	t.Run("Use existing context if not nil", func(t *testing.T) {
+		comp := &Component{
+			engine: definition.NewWorkloadAbstractEngine("test"),
+			FullTemplate: &Template{
+				TemplateStr: `
+output: {}
+parameter: {name: string}
+`,
+			},
+			Params: map[string]interface{}{"name": "test-name"},
+		}
+
+		// First call to populate context
+		ctx1, err := PrepareProcessContext(comp, ctxData)
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx1)
+
+		// Second call
+		ctx2, err := PrepareProcessContext(comp, ctxData)
+		assert.NoError(t, err)
+		assert.NotNil(t, ctx2)
+
+		// Check if the context object is the same
+		assert.Same(t, ctx1, ctx2)
+	})
+}
+
+func TestLoadDynamicComponent(t *testing.T) {
+	ctx := context.Background()
+	cli := fake.NewClientBuilder().Build()
+
+	t.Run("Non-ref-objects component", func(t *testing.T) {
+		af := &Appfile{}
+		comp := &common.ApplicationComponent{
+			Type: "webservice",
+		}
+		resultComp, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.NoError(t, err)
+		assert.Equal(t, comp, resultComp)
+	})
+
+	t.Run("Invalid properties", func(t *testing.T) {
+		af := &Appfile{}
+		comp := &common.ApplicationComponent{
+			Type: v1alpha1.RefObjectsComponentType,
+			Properties: &runtime.RawExtension{
+				Raw: []byte(`{"objects": "not-an-array"}`),
+			},
+		}
+		_, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "invalid ref-objects component properties")
+	})
+
+	t.Run("Success with URL selectors", func(t *testing.T) {
+		objWithURL := &unstructured.Unstructured{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "Secret",
+				"metadata": map[string]interface{}{
+					"name":      "secret1",
+					"namespace": "default",
+					"annotations": map[string]interface{}{
+						oam.AnnotationResourceURL: "http://example.com/secret.yaml",
+					},
+				},
+			},
+		}
+		af := &Appfile{
+			Namespace:       "default",
+			ReferredObjects: []*unstructured.Unstructured{objWithURL},
+			app:             &v1beta1.Application{},
+		}
+		comp := &common.ApplicationComponent{
+			Name: "my-comp",
+			Type: v1alpha1.RefObjectsComponentType,
+			Properties: &runtime.RawExtension{
+				Raw: []byte(`{"urls": ["http://example.com/secret.yaml"]}`),
+			},
+		}
+
+		resultComp, err := af.LoadDynamicComponent(ctx, cli, comp)
+		assert.NoError(t, err)
+
+		refObjList := &common.ReferredObjectList{}
+		err = json.Unmarshal(resultComp.Properties.Raw, refObjList)
+		assert.NoError(t, err)
+		assert.Len(t, refObjList.Objects, 1)
+
+		u := &unstructured.Unstructured{}
+		err = u.UnmarshalJSON(refObjList.Objects[0].Raw)
+		assert.NoError(t, err)
+		assert.Equal(t, "Secret", u.GetKind())
+		assert.Equal(t, "secret1", u.GetName())
+	})
+}
+
+func TestPolicyClient(t *testing.T) {
+	ctx := context.Background()
+	policy := &v1alpha1.Policy{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-policy", Namespace: "default"},
+		Type:       "override",
+	}
+	policyKey := client.ObjectKeyFromObject(policy)
+
+	cm := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"},
+		Data:       map[string]string{"key": "value"},
+	}
+	cmKey := client.ObjectKeyFromObject(cm)
+
+	t.Run("With AppRevision, policy in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+			ExternalPolicies: map[string]*v1alpha1.Policy{
+				policyKey.String(): policy,
+			},
+		}
+		cli := fake.NewClientBuilder().Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.NoError(t, err)
+		assert.Equal(t, policy, retrievedPolicy)
+	})
+
+	t.Run("With AppRevision, policy not in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision:      &v1beta1.ApplicationRevision{},
+			ExternalPolicies: map[string]*v1alpha1.Policy{},
+		}
+		// The policy exists in the client, but it should not be returned
+		cli := fake.NewClientBuilder().WithObjects(policy).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("With AppRevision, get non-policy object", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := policyClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+
+	t.Run("Without AppRevision, policy in client", func(t *testing.T) {
+		af := &Appfile{
+			ExternalPolicies: make(map[string]*v1alpha1.Policy),
+		}
+		cli := fake.NewClientBuilder().WithObjects(policy).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.NoError(t, err)
+		assert.Equal(t, policy, retrievedPolicy)
+
+		// Check if the policy is now cached
+		cachedPolicy, found := af.ExternalPolicies[policyKey.String()]
+		assert.True(t, found)
+		assert.Equal(t, policy, cachedPolicy)
+	})
+
+	t.Run("Without AppRevision, policy not in client", func(t *testing.T) {
+		af := &Appfile{
+			ExternalPolicies: make(map[string]*v1alpha1.Policy),
+		}
+		cli := fake.NewClientBuilder().Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedPolicy := &v1alpha1.Policy{}
+		err := policyClient.Get(ctx, policyKey, retrievedPolicy)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("Without AppRevision, get non-policy object", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		policyClient := af.PolicyClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := policyClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+}
+
+func TestWorkflowClient(t *testing.T) {
+	ctx := context.Background()
+	workflow := &workflowv1alpha1.Workflow{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-workflow", Namespace: "default"},
+	}
+	workflowKey := client.ObjectKeyFromObject(workflow)
+
+	cm := &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"},
+		Data:       map[string]string{"key": "value"},
+	}
+	cmKey := client.ObjectKeyFromObject(cm)
+
+	t.Run("With AppRevision, workflow in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision:      &v1beta1.ApplicationRevision{},
+			ExternalWorkflow: workflow,
+		}
+		cli := fake.NewClientBuilder().Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.NoError(t, err)
+		assert.Equal(t, workflow, retrievedWorkflow)
+	})
+
+	t.Run("With AppRevision, workflow not in cache", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		// The workflow exists in the client, but it should not be returned
+		cli := fake.NewClientBuilder().WithObjects(workflow).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("With AppRevision, get non-workflow object", func(t *testing.T) {
+		af := &Appfile{
+			AppRevision: &v1beta1.ApplicationRevision{},
+		}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := workflowClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+
+	t.Run("Without AppRevision, workflow in client", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(workflow).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.NoError(t, err)
+		assert.Equal(t, workflow, retrievedWorkflow)
+
+		// Check if the workflow is now cached
+		assert.Equal(t, workflow, af.ExternalWorkflow)
+	})
+
+	t.Run("Without AppRevision, workflow not in client", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedWorkflow := &workflowv1alpha1.Workflow{}
+		err := workflowClient.Get(ctx, workflowKey, retrievedWorkflow)
+		assert.Error(t, err)
+		assert.True(t, kerrors.IsNotFound(err))
+	})
+
+	t.Run("Without AppRevision, get non-workflow object", func(t *testing.T) {
+		af := &Appfile{}
+		cli := fake.NewClientBuilder().WithObjects(cm).Build()
+		workflowClient := af.WorkflowClient(cli)
+
+		retrievedCm := &corev1.ConfigMap{}
+		err := workflowClient.Get(ctx, cmKey, retrievedCm)
+		assert.NoError(t, err)
+		assert.Equal(t, cm, retrievedCm)
+	})
+}
+
+func TestSetWorkloadRefToTrait(t *testing.T) {
+	wlRef := corev1.ObjectReference{
+		APIVersion: "apps/v1",
+		Kind:       "Deployment",
+		Name:       "my-workload",
+	}
+
+	tests := map[string]struct {
+		reason    string
+		appfile   *Appfile
+		trait     *unstructured.Unstructured
+		wantTrait *unstructured.Unstructured
+		wantErr   error
+	}{
+		"AuxiliaryWorkloadTrait": {
+			reason:  "Should do nothing for auxiliary-workload traits",
+			appfile: &Appfile{},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: definition.AuxiliaryWorkload,
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: definition.AuxiliaryWorkload,
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"TraitDefinitionNotFound": {
+			reason: "Should return error if TraitDefinition is not found",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: errors.Errorf("TraitDefinition %s not found in appfile", "my-trait"),
+		},
+		"WorkloadRefPathIsEmpty": {
+			reason: "Should do nothing if workloadRefPath is empty",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"SetWorkloadRefSucceeds": {
+			reason: "Should set workload reference correctly",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec.workloadRef",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+				"spec": map[string]interface{}{},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+				"spec": map[string]interface{}{
+					"workloadRef": map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"name":       "my-workload",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"SetWorkloadRefWithSuffixedTraitType": {
+			reason: "Should find the base trait definition and set workload reference",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec.workloadRef",
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait-abcde",
+					},
+				},
+				"spec": map[string]interface{}{},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait-abcde",
+					},
+				},
+				"spec": map[string]interface{}{
+					"workloadRef": map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"name":       "my-workload",
+					},
+				},
+			}},
+			wantErr: nil,
+		},
+		"InvalidWorkloadRefPath": {
+			reason: "Should return an error for an invalid workloadRefPath",
+			appfile: &Appfile{
+				RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": {
+						Spec: v1beta1.TraitDefinitionSpec{
+							WorkloadRefPath: "spec[workloadRef", // Invalid path
+						},
+					},
+				},
+			},
+			trait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantTrait: &unstructured.Unstructured{Object: map[string]interface{}{
+				"metadata": map[string]interface{}{
+					"labels": map[string]interface{}{
+						oam.TraitTypeLabel: "my-trait",
+					},
+				},
+			}},
+			wantErr: fieldpath.Pave(map[string]interface{}{}).SetValue("spec[workloadRef", wlRef),
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			traitCopy := tc.trait.DeepCopy()
+			err := tc.appfile.setWorkloadRefToTrait(wlRef, traitCopy)
+
+			if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
+				t.Errorf("\n%s\nsetWorkloadRefToTrait(...): -want error, +got error:\n%s\n", tc.reason, diff)
+			}
+			if diff := cmp.Diff(tc.wantTrait, traitCopy); diff != "" {
+				t.Errorf("\n%s\nsetWorkloadRefToTrait(...): -want trait, +got trait:\n%s\n", tc.reason, diff)
+			}
+		})
+	}
+}
+
+func TestSetOAMContract(t *testing.T) {
+	baseAppfile := &Appfile{
+		Name:            "test-app",
+		Namespace:       "test-ns",
+		AppRevisionName: "test-app-v1",
+		AppLabels: map[string]string{
+			"app-label": "app-label-val",
+		},
+		AppAnnotations: map[string]string{
+			"app-annot": "app-annot-val",
+		},
+		RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+			"my-trait": {
+				Spec: v1beta1.TraitDefinitionSpec{
+					WorkloadRefPath: "spec.workloadRef",
+				},
+			},
+			"no-ref-trait": {
+				Spec: v1beta1.TraitDefinitionSpec{},
+			},
+		},
+	}
+
+	baseWorkload := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "apps/v1",
+			"kind":       "Deployment",
+			"metadata": map[string]interface{}{
+				"name": "my-workload",
+			},
+		},
+	}
+
+	baseTrait := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"labels": map[string]interface{}{
+					oam.TraitTypeLabel: "my-trait",
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		reason   string
+		appfile  *Appfile
+		comp     *oamtypes.ComponentManifest
+		wantComp *oamtypes.ComponentManifest
+		wantErr  bool
+	}{
+		"SuccessBasic": {
+			reason:  "A basic workload and trait should be assembled correctly",
+			appfile: baseAppfile,
+			comp: &oamtypes.ComponentManifest{
+				Name:            "my-comp",
+				ComponentOutput: baseWorkload.DeepCopy(),
+				ComponentOutputsAndTraits: []*unstructured.Unstructured{
+					baseTrait.DeepCopy(),
+				},
+			},
+			wantComp: &oamtypes.ComponentManifest{
+				Name: "my-comp",
+				ComponentOutput: &unstructured.Unstructured{
+					Object: map[string]interface{}{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"metadata": map[string]interface{}{
+							"name":      "my-workload",
+							"namespace": "test-ns",
+							"labels": map[string]interface{}{
+								"app-label":              "app-label-val",
+								oam.LabelAppName:         "test-app",
+								oam.LabelAppNamespace:    "test-ns",
+								oam.LabelAppRevision:     "test-app-v1",
+								oam.LabelAppComponent:    "my-comp",
+								oam.LabelOAMResourceType: oam.ResourceTypeWorkload,
+							},
+							"annotations": map[string]interface{}{
+								"app-annot": "app-annot-val",
+							},
+						},
+					},
+				},
+				ComponentOutputsAndTraits: []*unstructured.Unstructured{
+					{
+						Object: map[string]interface{}{
+							"apiVersion": "v1",
+							"kind":       "Service",
+							"metadata": map[string]interface{}{
+								"name":      "", // Name will be generated
+								"namespace": "test-ns",
+								"labels": map[string]interface{}{
+									"app-label":              "app-label-val",
+									oam.TraitTypeLabel:       "my-trait",
+									oam.LabelAppName:         "test-app",
+									oam.LabelAppNamespace:    "test-ns",
+									oam.LabelAppRevision:     "test-app-v1",
+									oam.LabelAppComponent:    "my-comp",
+									oam.LabelOAMResourceType: oam.ResourceTypeTrait,
+								},
+								"annotations": map[string]interface{}{
+									"app-annot": "app-annot-val",
+								},
+							},
+							"spec": map[string]interface{}{
+								"workloadRef": map[string]interface{}{
+									"apiVersion": "apps/v1",
+									"kind":       "Deployment",
+									"name":       "my-workload",
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			compCopy := deepCopyComponentManifest(tc.comp)
+			err := tc.appfile.SetOAMContract(compCopy)
+
+			if (err != nil) != tc.wantErr {
+				t.Errorf("\n%s\nSetOAMContract(...): want error? %v, got error: %v\n", tc.reason, tc.wantErr, err)
+			}
+
+			if name == "SuccessBasic" || name == "SuccessNameGeneration" {
+				for i, trait := range compCopy.ComponentOutputsAndTraits {
+					gotName := trait.GetName()
+					wantTrait := tc.wantComp.ComponentOutputsAndTraits[i]
+					traitType := trait.GetLabels()[oam.TraitTypeLabel]
+					expectedPrefix := compCopy.Name + "-" + traitType + "-"
+
+					if !strings.HasPrefix(gotName, expectedPrefix) {
+						t.Errorf("\n%s\nSetOAMContract(...): trait name %q does not have expected prefix %q", tc.reason, gotName, expectedPrefix)
+					}
+					trait.SetName("")
+					wantTrait.SetName("")
+				}
+			}
+
+			if !tc.wantErr {
+				if diff := cmp.Diff(tc.wantComp.ComponentOutput, compCopy.ComponentOutput); diff != "" {
+					t.Errorf("\n%s\nSetOAMContract(...): -want workload, +got workload:\n%s\n", tc.reason, diff)
+				}
+				if diff := cmp.Diff(tc.wantComp.ComponentOutputsAndTraits, compCopy.ComponentOutputsAndTraits); diff != "" {
+					t.Errorf("\n%s\nSetOAMContract(...): -want traits, +got traits:\n%s\n", tc.reason, diff)
+				}
+			}
+		})
+	}
+}
+
+func deepCopyComponentManifest(in *oamtypes.ComponentManifest) *oamtypes.ComponentManifest {
+	if in == nil {
+		return nil
+	}
+	out := &oamtypes.ComponentManifest{
+		Name: in.Name,
+	}
+	if in.ComponentOutput != nil {
+		out.ComponentOutput = in.ComponentOutput.DeepCopy()
+	}
+	if in.ComponentOutputsAndTraits != nil {
+		out.ComponentOutputsAndTraits = make([]*unstructured.Unstructured, len(in.ComponentOutputsAndTraits))
+		for i, trait := range in.ComponentOutputsAndTraits {
+			if trait != nil {
+				out.ComponentOutputsAndTraits[i] = trait.DeepCopy()
+			}
+		}
+	}
+	return out
+}
+
+func TestGenerateComponentManifests(t *testing.T) {
+	baseEngine := definition.NewWorkloadAbstractEngine("test-engine")
+	baseTraitEngine := definition.NewTraitAbstractEngine("scaler")
+	baseTemplate := &Template{
+		TemplateStr: `
+			output: {
+				apiVersion: "apps/v1"
+				kind:       "Deployment"
+				spec: {
+					selector: matchLabels: {
+						"app.oam.dev/component": context.name
+					}
+					template: {
+						metadata: labels: {
+							"app.oam.dev/component": context.name
+						}
+						spec: containers: [{
+							name:  context.name
+							image: parameter.image
+						}]
+					}
+				}
+			}
+			parameter: {
+				image: string
+			}`,
+	}
+	baseTraitTemplate := &Trait{
+		Name:   "scaler",
+		engine: baseTraitEngine,
+		Template: `
+			outputs: scaler: {
+				apiVersion: "autoscaling/v2beta2"
+				kind:       "HorizontalPodAutoscaler"
+				spec: {
+					scaleTargetRef: {
+						apiVersion: "apps/v1"
+						kind:       "Deployment"
+						name:       context.name
+					}
+					minReplicas: parameter.min
+					maxReplicas: parameter.max
+				}
+			}
+			parameter: {
+				min: *1 | int
+				max: *10 | int
+			}
+		`,
+		Params: map[string]interface{}{
+			"min": 2,
+			"max": 15,
+		},
+	}
+
+	type fields struct {
+		Appfile *Appfile
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr bool
+	}{
+		{
+			name: "Test generate component manifests",
+			fields: fields{
+				Appfile: &Appfile{
+					Name:            "test-app",
+					Namespace:       "test-ns",
+					AppRevisionName: "test-app-v1",
+					ParsedComponents: []*Component{
+						{
+							Name:   "test-comp",
+							Type:   "worker",
+							Params: map[string]interface{}{"image": "nginx:1.10"},
+							engine: baseEngine,
+							FullTemplate: &Template{
+								TemplateStr: baseTemplate.TemplateStr,
+							},
+							Traits: []*Trait{baseTraitTemplate},
+						},
+					},
+					RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
+						"scaler": {
+							Spec: v1beta1.TraitDefinitionSpec{
+								WorkloadRefPath: "spec.scaleTargetRef",
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			af := tt.fields.Appfile
+			got, err := af.GenerateComponentManifests()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GenerateComponentManifests() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			assert.NoError(t, err)
+			assert.Equal(t, len(af.ParsedComponents), len(got))
+			assert.Equal(t, "test-comp", got[0].Name)
+			assert.Equal(t, "Deployment", got[0].ComponentOutput.GetKind())
+			assert.Equal(t, "test-comp", got[0].ComponentOutput.GetName())
+			assert.Equal(t, "test-ns", got[0].ComponentOutput.GetNamespace())
+			assert.Equal(t, "test-app", got[0].ComponentOutput.GetLabels()[oam.LabelAppName])
+			assert.Equal(t, 1, len(got[0].ComponentOutputsAndTraits))
+			trait := got[0].ComponentOutputsAndTraits[0]
+			assert.Equal(t, "HorizontalPodAutoscaler", trait.GetKind())
+			assert.True(t, strings.HasPrefix(trait.GetName(), "test-comp-scaler-"))
+			paved := fieldpath.Pave(trait.Object)
+			name, err := paved.GetString("spec.scaleTargetRef.name")
+			assert.NoError(t, err)
+			assert.Equal(t, "test-comp", name)
+			assert.Equal(t, af.Artifacts, got)
+		})
+	}
+}
+
+func TestGeneratePolicyManifests(t *testing.T) {
+	policyEngine := definition.NewWorkloadAbstractEngine("test-policy")
+	policyTemplate := &Template{
+		TemplateStr: `
+			output: {
+				apiVersion: "v1"
+				kind:       "ConfigMap"
+				metadata: {
+					name: context.name
+				}
+				data: {
+					key: parameter.value
+				}
+			}
+			parameter: {
+				value: string
+			}
+		`,
+	}
+
+	af := &Appfile{
+		Name:            "test-app",
+		Namespace:       "test-ns",
+		AppRevisionName: "test-app-v1",
+		ParsedPolicies: []*Component{
+			{
+				Name:   "test-policy",
+				Type:   "override",
+				Params: map[string]interface{}{"value": "test-value"},
+				engine: policyEngine,
+				FullTemplate: &Template{
+					TemplateStr: policyTemplate.TemplateStr,
+				},
+			},
+		},
+		AppLabels: map[string]string{
+			"label-key": "label-value",
+		},
+	}
+
+	manifests, err := af.GeneratePolicyManifests(context.Background())
+	assert.NoError(t, err)
+	assert.Equal(t, 1, len(manifests))
+
+	cm := manifests[0]
+	assert.Equal(t, "v1", cm.GetAPIVersion())
+	assert.Equal(t, "ConfigMap", cm.GetKind())
+	assert.Equal(t, "test-policy", cm.GetName())
+	assert.Equal(t, "test-ns", cm.GetNamespace())
+	assert.Equal(t, "test-app", cm.GetLabels()[oam.LabelAppName])
+
+	data, found, err := unstructured.NestedStringMap(cm.Object, "data")
+	assert.NoError(t, err)
+	assert.True(t, found)
+	assert.Equal(t, "test-value", data["key"])
+}
diff --git a/pkg/appfile/dryrun/dryrun_test.go b/pkg/appfile/dryrun/dryrun_test.go
index 870fc8ca..f97416b3 100644
--- a/pkg/appfile/dryrun/dryrun_test.go
+++ b/pkg/appfile/dryrun/dryrun_test.go
@@ -61,6 +61,20 @@ var _ = Describe("Test DryRun", func() {
 		diff := cmp.Diff(&expC, comps[0])
 		Expect(diff).Should(BeEmpty())
 	})
+
+	It("Test ValidateApp forces default namespace (validation object) but ExecuteDryRun keeps app namespace", func() {
+		err := dryrunOpt.ValidateApp(context.Background(), "./testdata/dryrun-app-with-ns.yaml")
+		Expect(err).Should(BeNil())
+		appYAML := readDataFromFile("./testdata/dryrun-app-with-ns.yaml")
+		app := &v1beta1.Application{}
+		b, err2 := yaml.YAMLToJSON([]byte(appYAML))
+		Expect(err2).Should(BeNil())
+		err2 = json.Unmarshal(b, app)
+		Expect(err2).Should(BeNil())
+		comps, _, err3 := dryrunOpt.ExecuteDryRun(context.Background(), app)
+		Expect(err3).Should(BeNil())
+		Expect(comps[0].ComponentOutput.GetNamespace()).Should(Equal("custom-ns"))
+	})
 })
 
 var _ = Describe("Test dry run with policies", func() {
diff --git a/pkg/appfile/dryrun/testdata/dryrun-app-with-ns.yaml b/pkg/appfile/dryrun/testdata/dryrun-app-with-ns.yaml
new file mode 100644
index 00000000..a983d435
--- /dev/null
+++ b/pkg/appfile/dryrun/testdata/dryrun-app-with-ns.yaml
@@ -0,0 +1,14 @@
+apiVersion: core.oam.dev/v1beta1
+kind: Application
+metadata:
+  name: app-dryrun-ns
+  namespace: custom-ns
+spec:
+  components:
+    - name: myweb
+      type: myworker
+      properties:
+        image: busybox
+        cmd:
+          - sleep
+          - "1000"
diff --git a/pkg/appfile/parser_test.go b/pkg/appfile/parser_test.go
index 36953f13..6d431b1f 100644
--- a/pkg/appfile/parser_test.go
+++ b/pkg/appfile/parser_test.go
@@ -24,8 +24,7 @@ import (
 	"testing"
 
 	"github.com/crossplane/crossplane-runtime/pkg/test"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
 	"github.com/stretchr/testify/assert"
 	errors2 "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -34,16 +33,217 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"sigs.k8s.io/yaml"
 
-	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
-
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
-	"github.com/oam-dev/kubevela/apis/types"
-
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 	common2 "github.com/oam-dev/kubevela/pkg/utils/common"
 )
 
+func TestParsePolicies(t *testing.T) {
+	overrideCompDef := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "webservice", Namespace: "vela-system"},
+		Spec:       v1beta1.ComponentDefinitionSpec{Workload: common.WorkloadTypeDescriptor{Type: "Deployment"}},
+	}
+	customPolicyDef := &v1beta1.PolicyDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "custom-policy", Namespace: "vela-system"},
+		Spec: v1beta1.PolicyDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+		},
+	}
+	schemes := runtime.NewScheme()
+	v1beta1.AddToScheme(schemes)
+
+	testcases := []struct {
+		name           string
+		appfile        *Appfile
+		client         client.Client
+		wantErrContain string
+		assertFunc     func(*testing.T, *Appfile)
+	}{
+		{
+			name: "policy with nil properties",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "gc-policy",
+								Type:       v1alpha1.GarbageCollectPolicyType,
+								Properties: nil,
+							},
+						},
+					},
+				},
+			},
+			client:         fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErrContain: "must not have empty properties",
+		},
+		{
+			name: "debug policy",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "debug-policy",
+								Type: v1alpha1.DebugPolicyType,
+							},
+						},
+					},
+				},
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.True(t, af.Debug)
+			},
+		},
+		{
+			name: "override policy fails to get definition",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Components: []common.ApplicationComponent{
+							{
+								Name: "comp1",
+								Type: "webservice",
+							},
+						},
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "override-policy",
+								Type: v1alpha1.OverridePolicyType,
+								Properties: util.Object2RawExtension(v1alpha1.OverridePolicySpec{
+									Components: []v1alpha1.EnvComponentPatch{
+										{
+											Name: "comp1",
+											Type: "webservice",
+										},
+									},
+								}),
+							},
+						},
+					},
+				},
+				RelatedComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition),
+				RelatedTraitDefinitions:     make(map[string]*v1beta1.TraitDefinition),
+			},
+			client: &test.MockClient{
+				MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+					return fmt.Errorf("get definition error")
+				},
+			},
+			wantErrContain: "get definition error",
+		},
+		{
+			name: "override policy success",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Components: []common.ApplicationComponent{
+							{
+								Name: "comp1",
+								Type: "webservice",
+							},
+						},
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name: "override-policy",
+								Type: v1alpha1.OverridePolicyType,
+								Properties: util.Object2RawExtension(v1alpha1.OverridePolicySpec{
+									Components: []v1alpha1.EnvComponentPatch{
+										{
+											Name: "comp1",
+											Type: "webservice",
+										},
+									},
+								}),
+							},
+						},
+					},
+				},
+				RelatedComponentDefinitions: make(map[string]*v1beta1.ComponentDefinition),
+				RelatedTraitDefinitions:     make(map[string]*v1beta1.TraitDefinition),
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).WithObjects(overrideCompDef).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.Contains(t, af.RelatedComponentDefinitions, "webservice")
+			},
+		},
+		{
+			name: "custom policy definition not found",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "my-policy",
+								Type:       "custom-policy",
+								Properties: util.Object2RawExtension(map[string]string{"name": "test"}),
+							},
+						},
+					},
+				},
+			},
+			client: &test.MockClient{
+				MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+					if _, ok := obj.(*v1beta1.PolicyDefinition); ok {
+						return errors2.NewNotFound(v1beta1.Resource("policydefinition"), "custom-policy")
+					}
+					return nil
+				},
+			},
+			wantErrContain: "fetch component/policy type of my-policy",
+		},
+		{
+			name: "custom policy success",
+			appfile: &Appfile{
+				app: &v1beta1.Application{
+					Spec: v1beta1.ApplicationSpec{
+						Policies: []v1beta1.AppPolicy{
+							{
+								Name:       "my-policy",
+								Type:       "custom-policy",
+								Properties: util.Object2RawExtension(map[string]string{"name": "test"}),
+							},
+						},
+					},
+				},
+			},
+			client: fake.NewClientBuilder().WithScheme(schemes).WithObjects(customPolicyDef).Build(),
+			assertFunc: func(t *testing.T, af *Appfile) {
+				assert.Equal(t, 1, len(af.ParsedPolicies))
+				assert.Equal(t, "my-policy", af.ParsedPolicies[0].Name)
+				assert.Equal(t, "custom-policy", af.ParsedPolicies[0].Type)
+			},
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			p := NewApplicationParser(tc.client)
+			// This function is tested separated, mock it for parsePolicies
+			if tc.appfile.app != nil {
+				tc.appfile.Policies = tc.appfile.app.Spec.Policies
+			}
+			err := p.parsePolicies(context.Background(), tc.appfile)
+
+			if tc.wantErrContain != "" {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.wantErrContain)
+			} else {
+				assert.NoError(t, err)
+				if tc.assertFunc != nil {
+					tc.assertFunc(t, tc.appfile)
+				}
+			}
+		})
+	}
+}
+
 var expectedExceptApp = &Appfile{
 	Name: "application-sample",
 	ParsedComponents: []*Component{
@@ -81,10 +281,10 @@ var expectedExceptApp = &Appfile{
       			}
       		}
       
-      		selector:
-      			matchLabels:
-      				"app.oam.dev/component": context.name
-      	}
+      			selector:
+      				matchLabels:
+      					"app.oam.dev/component": context.name
+      		}
       }
       
       parameter: {
@@ -149,7 +349,7 @@ spec:
       		selector:
       			matchLabels:
       				"app.oam.dev/component": context.name
-      	}
+      		}
       }
 
       parameter: {
@@ -235,11 +435,11 @@ spec:
       properties:
 `
 
-var _ = Describe("Test application parser", func() {
-	It("Test parse an application", func() {
+func TestApplicationParser(t *testing.T) {
+	t.Run("Test parse an application", func(t *testing.T) {
 		o := v1beta1.Application{}
 		err := yaml.Unmarshal([]byte(appfileYaml), &o)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 
 		// Create a mock client
 		tclient := test.MockClient{
@@ -266,24 +466,24 @@ var _ = Describe("Test application parser", func() {
 		}
 
 		appfile, err := NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &o)
-		Expect(err).ShouldNot(HaveOccurred())
-		Expect(equal(expectedExceptApp, appfile)).Should(BeTrue())
+		assert.NoError(t, err)
+		assert.True(t, equal(expectedExceptApp, appfile))
 
 		notfound := v1beta1.Application{}
 		err = yaml.Unmarshal([]byte(appfileYaml2), &notfound)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 		_, err = NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &notfound)
-		Expect(err).Should(HaveOccurred())
+		assert.Error(t, err)
 
-		By("app with empty policy")
+		t.Log("app with empty policy")
 		emptyPolicy := v1beta1.Application{}
 		err = yaml.Unmarshal([]byte(appfileYamlEmptyPolicy), &emptyPolicy)
-		Expect(err).ShouldNot(HaveOccurred())
+		assert.NoError(t, err)
 		_, err = NewApplicationParser(&tclient).GenerateAppFile(context.TODO(), &emptyPolicy)
-		Expect(err).Should(HaveOccurred())
-		Expect(err.Error()).Should(ContainSubstring("have empty properties"))
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "have empty properties")
 	})
-})
+}
 
 func equal(af, dest *Appfile) bool {
 	if af.Name != dest.Name || len(af.ParsedComponents) != len(dest.ParsedComponents) {
@@ -313,29 +513,28 @@ func equal(af, dest *Appfile) bool {
 	return true
 }
 
-var _ = Describe("Test application parser", func() {
+func TestApplicationParserWithLegacyRevision(t *testing.T) {
 	var app v1beta1.Application
 	var apprev v1beta1.ApplicationRevision
 	var wsd v1beta1.WorkflowStepDefinition
 	var expectedExceptAppfile *Appfile
 	var mockClient test.MockClient
 
-	BeforeEach(func() {
-		// prepare WorkflowStepDefinition
-		Expect(common2.ReadYamlToObject("testdata/backport-1-2/wsd.yaml", &wsd)).Should(BeNil())
-
-		// prepare verify data
-		expectedExceptAppfile = &Appfile{
-			Name: "backport-1-2-test-demo",
-			ParsedComponents: []*Component{
-				{
-					Name: "backport-1-2-test-demo",
-					Type: "webservice",
-					Params: map[string]interface{}{
-						"image": "nginx",
-					},
-					FullTemplate: &Template{
-						TemplateStr: `
+	// prepare WorkflowStepDefinition
+	assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/wsd.yaml", &wsd))
+
+	// prepare verify data
+	expectedExceptAppfile = &Appfile{
+		Name: "backport-1-2-test-demo",
+		ParsedComponents: []*Component{
+			{
+				Name: "backport-1-2-test-demo",
+				Type: "webservice",
+				Params: map[string]interface{}{
+					"image": "nginx",
+				},
+				FullTemplate: &Template{
+					TemplateStr: `
       output: {
         apiVersion: "apps/v1"
       	kind:       "Deployment"
@@ -361,10 +560,10 @@ var _ = Describe("Test application parser", func() {
       			}
       		}
       
-      		selector:
-      			matchLabels:
-      				"app.oam.dev/component": context.name
-      	}
+      			selector:
+      				matchLabels:
+      					"app.oam.dev/component": context.name
+      		}
       }
       
       parameter: {
@@ -374,14 +573,14 @@ var _ = Describe("Test application parser", func() {
       
       	cmd?: [...string]
       }`,
-					},
-					Traits: []*Trait{
-						{
-							Name: "scaler",
-							Params: map[string]interface{}{
-								"replicas": float64(1),
-							},
-							Template: `
+				},
+				Traits: []*Trait{
+					{
+						Name: "scaler",
+						Params: map[string]interface{}{
+							"replicas": float64(1),
+						},
+						Template: `
 parameter: {
 	// +usage=Specify the number of workload
 	replicas: *1 | int
@@ -390,62 +589,59 @@ parameter: {
 patch: spec: replicas: parameter.replicas
 
 `,
-						},
 					},
 				},
 			},
-			WorkflowSteps: []workflowv1alpha1.WorkflowStep{
-				{
-					WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
-						Name: "apply",
-						Type: "apply-application",
-					},
+		},
+		WorkflowSteps: []workflowv1alpha1.WorkflowStep{
+			{
+				WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
+					Name: "apply",
+					Type: "apply-application",
 				},
 			},
-		}
+		},
+	}
 
-		// Create mock client
-		mockClient = test.MockClient{
-			MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
-				if strings.Contains(key.Name, "unknown") {
-					return &errors2.StatusError{ErrStatus: metav1.Status{Reason: "NotFound", Message: "not found"}}
-				}
-				switch o := obj.(type) {
-				case *v1beta1.ComponentDefinition:
-					wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
-					if err != nil {
-						return err
-					}
-					*o = *wd
-				case *v1beta1.WorkflowStepDefinition:
-					*o = wsd
-				case *v1beta1.ApplicationRevision:
-					*o = apprev
-				default:
-					// skip
+	// Create mock client
+	mockClient = test.MockClient{
+		MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
+			if strings.Contains(key.Name, "unknown") {
+				return &errors2.StatusError{ErrStatus: metav1.Status{Reason: "NotFound", Message: "not found"}}
+			}
+			switch o := obj.(type) {
+			case *v1beta1.ComponentDefinition:
+				wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
+				if err != nil {
+					return err
 				}
-				return nil
-			},
-		}
-	})
+				*o = *wd
+			case *v1beta1.WorkflowStepDefinition:
+				*o = wsd
+			case *v1beta1.ApplicationRevision:
+				*o = apprev
+			default:
+				// skip
+			}
+			return nil
+		},
+	}
 
-	When("with apply-application workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev1.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with apply-application workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev1.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 1", func() {
+		t.Run("Test we can parse an application revision to an appFile 1", func(t *testing.T) {
 
 			appfile, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).ShouldNot(HaveOccurred())
-			Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
-			Expect(len(appfile.WorkflowSteps) > 0 &&
-				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
+			assert.NoError(t, err)
+			assert.True(t, equal(expectedExceptAppfile, appfile))
+			assert.True(t, len(appfile.WorkflowSteps) > 0 &&
+				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions))
 
-			Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
+			assert.True(t, len(appfile.WorkflowSteps) > 0 && func() bool {
 				this := appfile.RelatedWorkflowStepDefinitions
 				that := appfile.AppRevision.Spec.WorkflowStepDefinitions
 				for i, w := range this {
@@ -455,27 +651,25 @@ patch: spec: replicas: parameter.replicas
 					}
 				}
 				return true
-			}()).Should(BeTrue())
+			}())
 		})
 	})
 
-	When("with apply-application and apply-component build-in workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev2.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with apply-application and apply-component build-in workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev2.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 2", func() {
+		t.Run("Test we can parse an application revision to an appFile 2", func(t *testing.T) {
 
 			appfile, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).ShouldNot(HaveOccurred())
-			Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
-			Expect(len(appfile.WorkflowSteps) > 0 &&
-				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
+			assert.NoError(t, err)
+			assert.True(t, equal(expectedExceptAppfile, appfile))
+			assert.True(t, len(appfile.WorkflowSteps) > 0 &&
+				len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions))
 
-			Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
+			assert.True(t, len(appfile.WorkflowSteps) > 0 && func() bool {
 				this := appfile.RelatedWorkflowStepDefinitions
 				that := appfile.AppRevision.Spec.WorkflowStepDefinitions
 				for i, w := range this {
@@ -486,29 +680,25 @@ patch: spec: replicas: parameter.replicas
 					}
 				}
 				return true
-			}()).Should(BeTrue())
+			}())
 		})
 	})
 
-	When("with unknown workflowStep", func() {
-		BeforeEach(func() {
-			// prepare application
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
-			// prepare application revision
-			Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev3.yaml", &apprev)).Should(BeNil())
-		})
+	t.Run("with unknown workflowStep", func(t *testing.T) {
+		// prepare application
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app))
+		// prepare application revision
+		assert.NoError(t, common2.ReadYamlToObject("testdata/backport-1-2/apprev3.yaml", &apprev))
 
-		It("Test we can parse an application revision to an appFile 3", func() {
+		t.Run("Test we can parse an application revision to an appFile 3", func(t *testing.T) {
 
 			_, err := NewApplicationParser(&mockClient).GenerateAppFile(context.TODO(), &app)
-			Expect(err).Should(HaveOccurred())
-			Expect(err.Error()).Should(SatisfyAll(
-				ContainSubstring("failed to get workflow step definition apply-application-unknown: not found"),
-				ContainSubstring("failed to parseWorkflowStepsForLegacyRevision")),
-			)
+			assert.Error(t, err)
+			assert.Contains(t, err.Error(), "failed to get workflow step definition apply-application-unknown: not found")
+			assert.Contains(t, err.Error(), "failed to parseWorkflowStepsForLegacyRevision")
 		})
 	})
-})
+}
 
 func TestParser_parseTraits(t *testing.T) {
 	type args struct {
@@ -707,3 +897,145 @@ func TestParser_parseTraitsFromRevision(t *testing.T) {
 		})
 	}
 }
+
+func TestParseComponentFromRevisionAndClient(t *testing.T) {
+	compDef := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "webservice", Namespace: "vela-system"},
+		Spec: v1beta1.ComponentDefinitionSpec{
+			Workload:  common.WorkloadTypeDescriptor{Type: "Deployment"},
+			Schematic: &common.Schematic{CUE: &common.CUE{Template: "parameter: {image: string}"}},
+		},
+	}
+	traitDef := &v1beta1.TraitDefinition{
+		ObjectMeta: metav1.ObjectMeta{Name: "scaler", Namespace: "vela-system"},
+		Spec: v1beta1.TraitDefinitionSpec{
+			Schematic: &common.Schematic{CUE: &common.CUE{Template: "parameter: {replicas: int}"}},
+		},
+	}
+
+	appComp := common.ApplicationComponent{
+		Name:       "my-comp",
+		Type:       "webservice",
+		Properties: util.Object2RawExtension(map[string]string{"image": "nginx"}),
+		Traits: []common.ApplicationTrait{
+			{
+				Type:       "scaler",
+				Properties: util.Object2RawExtension(map[string]int{"replicas": 2}),
+			},
+		},
+	}
+
+	schemes := runtime.NewScheme()
+	v1beta1.AddToScheme(schemes)
+
+	tests := []struct {
+		name       string
+		appRev     *v1beta1.ApplicationRevision
+		client     client.Client
+		wantErr    bool
+		assertFunc func(*testing.T, *Component)
+	}{
+		{
+			name: "component and trait found in revision",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+						TraitDefinitions:     map[string]*v1beta1.TraitDefinition{"scaler": traitDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "my-comp", c.Name)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name: "component not in revision, but in cluster",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						TraitDefinitions: map[string]*v1beta1.TraitDefinition{"scaler": traitDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(compDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name: "trait not in revision, but in cluster",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(traitDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name:    "component and trait not in revision, but in cluster",
+			appRev:  &v1beta1.ApplicationRevision{},
+			client:  fake.NewClientBuilder().WithScheme(schemes).WithObjects(compDef, traitDef).Build(),
+			wantErr: false,
+			assertFunc: func(t *testing.T, c *Component) {
+				assert.NotNil(t, c)
+				assert.Equal(t, "webservice", c.Type)
+				assert.Equal(t, 1, len(c.Traits))
+				assert.Equal(t, "scaler", c.Traits[0].Name)
+			},
+		},
+		{
+			name:    "component not found anywhere",
+			appRev:  &v1beta1.ApplicationRevision{},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: true,
+		},
+		{
+			name: "trait not found anywhere",
+			appRev: &v1beta1.ApplicationRevision{
+				Spec: v1beta1.ApplicationRevisionSpec{
+					ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+						ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{"webservice": compDef},
+					},
+				},
+			},
+			client:  fake.NewClientBuilder().WithScheme(schemes).Build(),
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := NewApplicationParser(tt.client)
+			comp, err := p.ParseComponentFromRevisionAndClient(context.Background(), appComp, tt.appRev)
+
+			if tt.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				if tt.assertFunc != nil {
+					tt.assertFunc(t, comp)
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/appfile/suit_test.go b/pkg/appfile/suit_test.go
deleted file mode 100644
index 871bc309..00000000
--- a/pkg/appfile/suit_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-Copyright 2021 The KubeVela 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 appfile
-
-import (
-	"path/filepath"
-	"testing"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/apps/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
-
-	coreoam "github.com/oam-dev/kubevela/apis/core.oam.dev"
-	// +kubebuilder:scaffold:imports
-)
-
-var cfg *rest.Config
-var scheme *runtime.Scheme
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAppFile(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "Cli Suite")
-}
-
-var _ = BeforeSuite(func() {
-	logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
-	By("bootstrapping test environment")
-	useExistCluster := false
-	testEnv = &envtest.Environment{
-		ControlPlaneStartTimeout: time.Minute,
-		ControlPlaneStopTimeout:  time.Minute,
-		CRDDirectoryPaths:        []string{filepath.Join("..", "..", "charts", "vela-core", "crds")},
-		UseExistingCluster:       &useExistCluster,
-	}
-
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ToNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-	scheme = runtime.NewScheme()
-	Expect(coreoam.AddToScheme(scheme)).NotTo(HaveOccurred())
-	Expect(clientgoscheme.AddToScheme(scheme)).NotTo(HaveOccurred())
-	Expect(v1.AddToScheme(scheme)).NotTo(HaveOccurred())
-	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
-	Expect(err).ToNot(HaveOccurred())
-	Expect(k8sClient).ToNot(BeNil())
-})
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
diff --git a/pkg/appfile/template_test.go b/pkg/appfile/template_test.go
index 0708f7dd..a87026ab 100644
--- a/pkg/appfile/template_test.go
+++ b/pkg/appfile/template_test.go
@@ -18,14 +18,19 @@ package appfile
 
 import (
 	"context"
+	"errors"
+	"fmt"
 	"testing"
 
 	"cuelang.org/go/cue/cuecontext"
 	"github.com/crossplane/crossplane-runtime/pkg/test"
 	"github.com/google/go-cmp/cmp"
 	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	ktypes "k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -35,6 +40,24 @@ import (
 	oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
 )
 
+type fakeRESTMapper struct {
+	meta.RESTMapper
+}
+
+func (f fakeRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+	if resource.Resource == "deployments" {
+		return schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, nil
+	}
+	return schema.GroupVersionKind{}, errors.New("no mapping for KindFor")
+}
+
+func (f fakeRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+	if resource.Resource == "deployments" {
+		return []schema.GroupVersionKind{{Group: "apps", Version: "v1", Kind: "Deployment"}}, nil
+	}
+	return nil, errors.New("no mapping for KindsFor")
+}
+
 func TestLoadComponentTemplate(t *testing.T) {
 	cueTemplate := `
       context: {
@@ -47,35 +70,35 @@ func TestLoadComponentTemplate(t *testing.T) {
       		selector: matchLabels: {
       			"app.oam.dev/component": context.name
       		}
-      
+
       		template: {
       			metadata: labels: {
       				"app.oam.dev/component": context.name
       			}
-      
+
       			spec: {
       				containers: [{
       					name:  context.name
       					image: parameter.image
-      
+
       					if parameter["cmd"] != _|_ {
       						command: parameter.cmd
       					}
       				}]
       			}
       		}
-      
+
       		selector:
       			matchLabels:
       				"app.oam.dev/component": context.name
       	}
       }
-      
+
       parameter: {
       	// +usage=Which image would you like to use for your service
       	// +short=i
       	image: string
-      
+
       	cmd?: [...string]
       }
       `
@@ -155,7 +178,7 @@ func TestLoadTraitTemplate(t *testing.T) {
         }
 
         outputs: ingress: {
-        	apiVersion: "networking.k8s.io/v1beta1"
+        	apiVersion: "networking.k8s.io/v1"
         	kind:       "Ingress"
         	metadata:
         		name: context.name
@@ -166,9 +189,14 @@ func TestLoadTraitTemplate(t *testing.T) {
         				paths: [
         					for k, v in parameter.http {
         						path: k
+        						pathType: "Prefix"
         						backend: {
-        							serviceName: context.name
-        							servicePort: v
+        							service: {
+        								name: context.name
+        								port: {
+        									number: v
+        								}
+        							}
         						}
         					},
         				]
@@ -311,7 +339,7 @@ metadata:
 spec:
   status:
     customStatus: testCustomStatus
-    healthPolicy: testHealthPolicy 
+    healthPolicy: testHealthPolicy
   workload:
     definition:
       apiVersion: apps/v1
@@ -328,7 +356,7 @@ metadata:
 spec:
   status:
     customStatus: testCustomStatus
-    healthPolicy: testHealthPolicy 
+    healthPolicy: testHealthPolicy
   appliesToWorkloads:
     - deployments.apps
   schematic:
@@ -380,3 +408,349 @@ spec:
 		t.Fatal("failed load template of trait definition ", diff)
 	}
 }
+
+func TestLoadTemplateFromRevision(t *testing.T) {
+	compDef := v1beta1.ComponentDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.ComponentDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-comp"},
+		Spec: v1beta1.ComponentDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			Workload: common.WorkloadTypeDescriptor{
+				Definition: common.WorkloadGVK{APIVersion: "v1", Kind: "Pod"},
+			},
+		},
+	}
+	traitDef := v1beta1.TraitDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.TraitDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-trait"},
+		Spec: v1beta1.TraitDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {port: int}"},
+			},
+		},
+	}
+	policyDef := v1beta1.PolicyDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.PolicyDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-policy"},
+		Spec: v1beta1.PolicyDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {replicas: int}"},
+			},
+		},
+	}
+	wfStepDef := v1beta1.WorkflowStepDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.WorkflowStepDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-step"},
+		Spec: v1beta1.WorkflowStepDefinitionSpec{
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {image: string}"},
+			},
+		},
+	}
+	wlDef := v1beta1.WorkloadDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       v1beta1.WorkloadDefinitionKind,
+			APIVersion: v1beta1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{Name: "my-workload"},
+		Spec: v1beta1.WorkloadDefinitionSpec{
+			Reference: common.DefinitionReference{
+				Name: "deployments.apps",
+			},
+			Schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "output: {apiVersion: 'apps/v1', kind: 'Deployment'}"},
+			},
+		},
+	}
+
+	appRev := &v1beta1.ApplicationRevision{
+		ObjectMeta: metav1.ObjectMeta{Name: "my-app-rev"},
+		Spec: v1beta1.ApplicationRevisionSpec{
+			ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
+				ComponentDefinitions: map[string]*v1beta1.ComponentDefinition{
+					"my-comp": &compDef,
+				},
+				TraitDefinitions: map[string]*v1beta1.TraitDefinition{
+					"my-trait": &traitDef,
+				},
+				PolicyDefinitions: map[string]v1beta1.PolicyDefinition{
+					"my-policy": policyDef,
+				},
+				WorkflowStepDefinitions: map[string]*v1beta1.WorkflowStepDefinition{
+					"my-step": &wfStepDef,
+				},
+				WorkloadDefinitions: map[string]v1beta1.WorkloadDefinition{
+					"my-workload": wlDef,
+				},
+			},
+		},
+	}
+
+	mapper := fakeRESTMapper{}
+
+	testCases := map[string]struct {
+		capName   string
+		capType   types.CapType
+		apprev    *v1beta1.ApplicationRevision
+		checkFunc func(t *testing.T, tmpl *Template, err error)
+	}{
+		"load component definition": {
+			capName: "my-comp",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {name: string}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.ComponentDefinitionKind, tmpl.ComponentDefinition.Kind)
+			},
+		},
+		"load trait definition": {
+			capName: "my-trait",
+			capType: types.TypeTrait,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {port: int}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.TraitDefinitionKind, tmpl.TraitDefinition.Kind)
+			},
+		},
+		"load policy definition": {
+			capName: "my-policy",
+			capType: types.TypePolicy,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {replicas: int}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.PolicyDefinitionKind, tmpl.PolicyDefinition.Kind)
+			},
+		},
+		"load workflow step definition": {
+			capName: "my-step",
+			capType: types.TypeWorkflowStep,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {image: string}", tmpl.TemplateStr)
+				assert.Equal(t, v1beta1.WorkflowStepDefinitionKind, tmpl.WorkflowStepDefinition.Kind)
+			},
+		},
+		"fallback to workload definition": {
+			capName: "my-workload",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "output: {apiVersion: 'apps/v1', kind: 'Deployment'}", tmpl.TemplateStr)
+				assert.NotNil(t, tmpl.WorkloadDefinition)
+				assert.Equal(t, v1beta1.WorkloadDefinitionKind, tmpl.WorkloadDefinition.Kind)
+				assert.Equal(t, "apps/v1", tmpl.Reference.Definition.APIVersion)
+				assert.Equal(t, "Deployment", tmpl.Reference.Definition.Kind)
+			},
+		},
+		"definition not found": {
+			capName: "not-exist",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.True(t, IsNotFoundInAppRevision(err))
+				assert.Contains(t, err.Error(), "component definition [not-exist] not found in app revision my-app-rev")
+			},
+		},
+		"nil app revision": {
+			capName: "any",
+			capType: types.TypeComponentDefinition,
+			apprev:  nil,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.Contains(t, err.Error(), "fail to find template for any as app revision is empty")
+			},
+		},
+		"unsupported type": {
+			capName: "any",
+			capType: "unsupported",
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.Error(t, err)
+				assert.Nil(t, tmpl)
+				assert.Contains(t, err.Error(), "kind(unsupported) of any not supported")
+			},
+		},
+		"verify revision name": {
+			capName: "my-comp@my-ns",
+			capType: types.TypeComponentDefinition,
+			apprev:  appRev,
+			checkFunc: func(t *testing.T, tmpl *Template, err error) {
+				assert.NoError(t, err)
+				assert.NotNil(t, tmpl)
+				assert.Equal(t, "parameter: {name: string}", tmpl.TemplateStr)
+			},
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			tmpl, err := LoadTemplateFromRevision(tc.capName, tc.capType, tc.apprev, mapper)
+			tc.checkFunc(t, tmpl, err)
+		})
+	}
+}
+
+func TestConvertTemplateJSON2Object(t *testing.T) {
+	testCases := map[string]struct {
+		capName   string
+		in        *runtime.RawExtension
+		schematic *common.Schematic
+		wantCap   types.Capability
+		wantErr   bool
+	}{
+		"with schematic CUE": {
+			capName: "test-cap",
+			schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap",
+				CueTemplate: "parameter: {name: string}",
+			},
+			wantErr: false,
+		},
+		"with RawExtension": {
+			capName: "test-cap-2",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"template": "parameter: {age: int}"}`),
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap-2",
+				CueTemplate: "parameter: {age: int}",
+			},
+			wantErr: false,
+		},
+		"with both schematic and RawExtension": {
+			capName: "test-cap-3",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"description": "test"}`),
+			},
+			schematic: &common.Schematic{
+				CUE: &common.CUE{Template: "parameter: {name: string}"},
+			},
+			wantCap: types.Capability{
+				Name:        "test-cap-3",
+				Description: "test",
+				CueTemplate: "parameter: {name: string}",
+			},
+			wantErr: false,
+		},
+		"with invalid JSON in RawExtension": {
+			capName: "test-cap-4",
+			in: &runtime.RawExtension{
+				Raw: []byte(`{"template": "parameter: {age: int}"`),
+			},
+			wantErr: true,
+		},
+		"with no template": {
+			capName: "test-cap-5",
+			in:      &runtime.RawExtension{Raw: []byte(`{"description": "test"}`)},
+			wantCap: types.Capability{
+				Name:        "test-cap-5",
+				Description: "test",
+			},
+			wantErr: false,
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			cap, err := ConvertTemplateJSON2Object(tc.capName, tc.in, tc.schematic)
+			if tc.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				if diff := cmp.Diff(tc.wantCap, cap); diff != "" {
+					t.Errorf("ConvertTemplateJSON2Object() (-want, +got):\n%s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestTemplateAsStatusRequest(t *testing.T) {
+	tmpl := &Template{
+		Health:       "isHealth: true",
+		CustomStatus: "message: 'Ready'",
+		Details:      "details: 'some details'",
+	}
+	params := map[string]interface{}{
+		"param1": "value1",
+	}
+	statusReq := tmpl.AsStatusRequest(params)
+
+	assert.Equal(t, "isHealth: true", statusReq.Health)
+	assert.Equal(t, "message: 'Ready'", statusReq.Custom)
+	assert.Equal(t, "details: 'some details'", statusReq.Details)
+	assert.Equal(t, params, statusReq.Parameter)
+}
+
+func TestIsNotFoundInAppRevision(t *testing.T) {
+	testCases := map[string]struct {
+		err      error
+		expected bool
+	}{
+		"component definition not found": {
+			err:      fmt.Errorf("component definition [my-comp] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"trait definition not found": {
+			err:      fmt.Errorf("trait definition [my-trait] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"policy definition not found": {
+			err:      fmt.Errorf("policy definition [my-policy] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"workflow step definition not found": {
+			err:      fmt.Errorf("workflow step definition [my-step] not found in app revision [my-app-rev]"),
+			expected: true,
+		},
+		"different error": {
+			err:      errors.New("a completely different error"),
+			expected: false,
+		},
+		"nil error": {
+			err:      nil,
+			expected: false,
+		},
+		"error with similar text but not exactly": {
+			err:      fmt.Errorf("this resource is not found in revision of app"),
+			expected: false,
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(t *testing.T) {
+			result := IsNotFoundInAppRevision(tc.err)
+			assert.Equal(t, tc.expected, result)
+		})
+	}
+}
diff --git a/pkg/appfile/unit_test.go b/pkg/appfile/unit_test.go
deleted file mode 100644
index 297559c0..00000000
--- a/pkg/appfile/unit_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-Copyright 2023 The KubeVela 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 appfile
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
-)
-
-func TestIsNotFoundInAppFile(t *testing.T) {
-	require.True(t, IsNotFoundInAppFile(fmt.Errorf("ComponentDefinition XXX not found in appfile")))
-}
-
-func TestIsNotFoundInAppRevision(t *testing.T) {
-	require.True(t, IsNotFoundInAppRevision(fmt.Errorf("ComponentDefinition XXX not found in app revision")))
-}
-
-func TestParseComponentFromRevisionAndClient(t *testing.T) {
-	ctx := context.Background()
-	cli := fake.NewClientBuilder().WithScheme(scheme).Build()
-	p := &Parser{
-		client:     cli,
-		tmplLoader: LoadTemplate,
-	}
-	comp := common.ApplicationComponent{
-		Name:       "test",
-		Type:       "test",
-		Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		Traits: []common.ApplicationTrait{{
-			Type:       "tr",
-			Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		}, {
-			Type:       "internal",
-			Properties: &runtime.RawExtension{Raw: []byte(`{}`)},
-		}},
-	}
-	appRev := &v1beta1.ApplicationRevision{}
-	cd := &v1beta1.ComponentDefinition{ObjectMeta: metav1.ObjectMeta{Name: "test"}}
-	td := &v1beta1.TraitDefinition{ObjectMeta: metav1.ObjectMeta{Name: "tr"}}
-	require.NoError(t, cli.Create(ctx, cd))
-	require.NoError(t, cli.Create(ctx, td))
-	appRev.Spec.TraitDefinitions = map[string]*v1beta1.TraitDefinition{"internal": {}}
-	_, err := p.ParseComponentFromRevisionAndClient(ctx, comp, appRev)
-	require.NoError(t, err)
-
-	_comp1 := comp.DeepCopy()
-	_comp1.Type = "bad"
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp1, appRev)
-	require.Error(t, err)
-
-	_comp2 := comp.DeepCopy()
-	_comp2.Traits[0].Type = "bad"
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp2, appRev)
-	require.Error(t, err)
-
-	_comp3 := comp.DeepCopy()
-	_comp3.Traits[0].Properties = &runtime.RawExtension{Raw: []byte(`bad`)}
-	_, err = p.ParseComponentFromRevisionAndClient(ctx, *_comp3, appRev)
-	require.Error(t, err)
-}
diff --git a/pkg/appfile/validate_test.go b/pkg/appfile/validate_test.go
index 4fe61bc7..7720a799 100644
--- a/pkg/appfile/validate_test.go
+++ b/pkg/appfile/validate_test.go
@@ -17,67 +17,32 @@ limitations under the License.
 package appfile
 
 import (
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+	"testing"
+
+	"cuelang.org/go/cue"
+	"github.com/stretchr/testify/assert"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
 
 	"github.com/oam-dev/kubevela/apis/types"
 	"github.com/oam-dev/kubevela/pkg/cue/definition"
+	"github.com/oam-dev/kubevela/pkg/features"
 )
 
-var _ = Describe("Test validate CUE schematic Appfile", func() {
+func TestTrait_EvalContext_OutputNameUniqueness(t *testing.T) {
 	type SubTestCase struct {
+		name          string
 		compDefTmpl   string
 		traitDefTmpl1 string
 		traitDefTmpl2 string
 		wantErrMsg    string
 	}
 
-	DescribeTable("Test validate outputs name unique", func(tc SubTestCase) {
-		Expect("").Should(BeEmpty())
-		wl := &Component{
-			Name:               "myweb",
-			Type:               "worker",
-			CapabilityCategory: types.CUECategory,
-			Traits: []*Trait{
-				{
-					Name:               "myscaler",
-					CapabilityCategory: types.CUECategory,
-					Template:           tc.traitDefTmpl1,
-					engine:             definition.NewTraitAbstractEngine("myscaler"),
-				},
-				{
-					Name:               "myingress",
-					CapabilityCategory: types.CUECategory,
-					Template:           tc.traitDefTmpl2,
-					engine:             definition.NewTraitAbstractEngine("myingress"),
-				},
-			},
-			FullTemplate: &Template{
-				TemplateStr: tc.compDefTmpl,
-			},
-			engine: definition.NewWorkloadAbstractEngine("myweb"),
-		}
-
-		ctxData := GenerateContextDataFromAppFile(&Appfile{
-			Name:            "myapp",
-			Namespace:       "test-ns",
-			AppRevisionName: "myapp-v1",
-		}, wl.Name)
-		pCtx, err := newValidationProcessContext(wl, ctxData)
-		Expect(err).Should(BeNil())
-		Eventually(func() string {
-			for _, tr := range wl.Traits {
-				if err := tr.EvalContext(pCtx); err != nil {
-					return err.Error()
-				}
-			}
-			return ""
-		}).Should(ContainSubstring(tc.wantErrMsg))
-	},
-		Entry("Succeed", SubTestCase{
+	testCases := []SubTestCase{
+		{
+			name: "Succeed",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc: {
@@ -98,11 +63,12 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: "",
-		}),
-		Entry("CompDef and TraitDef have same outputs", SubTestCase{
+		},
+		{
+			name: "CompDef and TraitDef have same outputs",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc1: {
@@ -123,11 +89,12 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: `auxiliary "mysvc1" already exits`,
-		}),
-		Entry("TraitDefs have same outputs", SubTestCase{
+		},
+		{
+			name: "TraitDefs have same outputs",
 			compDefTmpl: `
 			output: {
-				apiVersion: "apps/v1" 
+				apiVersion: "apps/v1"
 				kind: "Deployment"
 				}
 			outputs: mysvc: {
@@ -148,41 +115,72 @@ var _ = Describe("Test validate CUE schematic Appfile", func() {
 			}
 			`,
 			wantErrMsg: `auxiliary "mysvc1" already exits`,
-		}),
-	)
-})
+		},
+	}
 
-var _ = Describe("Test ValidateComponentParams", func() {
-	type ParamTestCase struct {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			wl := &Component{
+				Name:               "myweb",
+				Type:               "worker",
+				CapabilityCategory: types.CUECategory,
+				Traits: []*Trait{
+					{
+						Name:               "myscaler",
+						CapabilityCategory: types.CUECategory,
+						Template:           tc.traitDefTmpl1,
+						engine:             definition.NewTraitAbstractEngine("myscaler"),
+					},
+					{
+						Name:               "myingress",
+						CapabilityCategory: types.CUECategory,
+						Template:           tc.traitDefTmpl2,
+						engine:             definition.NewTraitAbstractEngine("myingress"),
+					},
+				},
+				FullTemplate: &Template{
+					TemplateStr: tc.compDefTmpl,
+				},
+				engine: definition.NewWorkloadAbstractEngine("myweb"),
+			}
+
+			ctxData := GenerateContextDataFromAppFile(&Appfile{
+				Name:            "myapp",
+				Namespace:       "test-ns",
+				AppRevisionName: "myapp-v1",
+			}, wl.Name)
+			pCtx, err := newValidationProcessContext(wl, ctxData)
+			assert.NoError(t, err)
+
+			var evalErr error
+			for _, tr := range wl.Traits {
+				if err := tr.EvalContext(pCtx); err != nil {
+					evalErr = err
+					break
+				}
+			}
+
+			if tc.wantErrMsg != "" {
+				assert.Error(t, evalErr)
+				assert.Contains(t, evalErr.Error(), tc.wantErrMsg)
+			} else {
+				assert.NoError(t, evalErr)
+			}
+		})
+	}
+}
+
+func TestParser_ValidateComponentParams(t *testing.T) {
+	testCases := []struct {
 		name     string
+		compName string
 		template string
 		params   map[string]interface{}
 		wantErr  string
-	}
-
-	DescribeTable("ValidateComponentParams cases", func(tc ParamTestCase) {
-		wl := &Component{
-			Name:         tc.name,
-			Type:         "worker",
-			FullTemplate: &Template{TemplateStr: tc.template},
-			Params:       tc.params,
-		}
-		app := &Appfile{
-			Name:      "myapp",
-			Namespace: "test-ns",
-		}
-		ctxData := GenerateContextDataFromAppFile(app, wl.Name)
-		parser := &Parser{}
-		err := parser.ValidateComponentParams(ctxData, wl, app)
-		if tc.wantErr == "" {
-			Expect(err).To(BeNil())
-		} else {
-			Expect(err).To(HaveOccurred())
-			Expect(err.Error()).To(ContainSubstring(tc.wantErr))
-		}
-	},
-		Entry("valid params and template", ParamTestCase{
-			name: "valid",
+	}{
+		{
+			name:     "valid params and template",
+			compName: "valid",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -196,9 +194,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": 2,
 			},
 			wantErr: "",
-		}),
-		Entry("invalid CUE in template", ParamTestCase{
-			name: "invalid-cue",
+		},
+		{
+			name:     "invalid CUE in template",
+			compName: "invalid-cue",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -213,9 +212,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": 2,
 			},
 			wantErr: "CUE compile error",
-		}),
-		Entry("missing required parameter", ParamTestCase{
-			name: "missing-required",
+		},
+		{
+			name:     "missing required parameter",
+			compName: "missing-required",
 			template: `
 			parameter: {
 				replicas: int
@@ -227,9 +227,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 			`,
 			params:  map[string]interface{}{},
 			wantErr: "component \"missing-required\": missing parameters: replicas",
-		}),
-		Entry("parameter constraint violation", ParamTestCase{
-			name: "constraint-violation",
+		},
+		{
+			name:     "parameter constraint violation",
+			compName: "constraint-violation",
 			template: `
 			parameter: {
 				replicas: int & >0
@@ -243,9 +244,10 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": -1,
 			},
 			wantErr: "parameter constraint violation",
-		}),
-		Entry("invalid parameter block", ParamTestCase{
-			name: "invalid-param-block",
+		},
+		{
+			name:     "invalid parameter block",
+			compName: "invalid-param-block",
 			template: `
 			parameter: {
 				replicas: int | *1
@@ -259,6 +261,340 @@ var _ = Describe("Test ValidateComponentParams", func() {
 				"replicas": "not-an-int",
 			},
 			wantErr: "parameter constraint violation",
-		}),
-	)
-})
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			wl := &Component{
+				Name:         tc.compName,
+				Type:         "worker",
+				FullTemplate: &Template{TemplateStr: tc.template},
+				Params:       tc.params,
+			}
+			app := &Appfile{
+				Name:      "myapp",
+				Namespace: "test-ns",
+			}
+			ctxData := GenerateContextDataFromAppFile(app, wl.Name)
+			parser := &Parser{}
+			err := parser.ValidateComponentParams(ctxData, wl, app)
+			if tc.wantErr == "" {
+				assert.NoError(t, err)
+			} else {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.wantErr)
+			}
+		})
+	}
+}
+
+func TestValidationHelpers(t *testing.T) {
+	t.Run("renderTemplate", func(t *testing.T) {
+		tmpl := "output: {}"
+		expected := "output: {}\ncontext: _\nparameter: _\n"
+		assert.Equal(t, expected, renderTemplate(tmpl))
+	})
+
+	t.Run("cueParamBlock", func(t *testing.T) {
+		t.Run("should handle empty params", func(t *testing.T) {
+			out, err := cueParamBlock(map[string]any{})
+			assert.NoError(t, err)
+			assert.Equal(t, "parameter: {}", out)
+		})
+
+		t.Run("should handle valid params", func(t *testing.T) {
+			params := map[string]any{"key": "value"}
+			out, err := cueParamBlock(params)
+			assert.NoError(t, err)
+			assert.Equal(t, `parameter: {"key":"value"}`, out)
+		})
+
+		t.Run("should return error for unmarshallable params", func(t *testing.T) {
+			params := map[string]any{"key": make(chan int)}
+			_, err := cueParamBlock(params)
+			assert.Error(t, err)
+		})
+	})
+
+	t.Run("filterMissing", func(t *testing.T) {
+		t.Run("should filter missing keys", func(t *testing.T) {
+			keys := []string{"a", "b.c", "d"}
+			provided := map[string]any{
+				"a": 1,
+				"b": map[string]any{
+					"c": 2,
+				},
+			}
+			out, err := filterMissing(keys, provided)
+			assert.NoError(t, err)
+			assert.Equal(t, []string{"d"}, out)
+		})
+
+		t.Run("should handle no missing keys", func(t *testing.T) {
+			keys := []string{"a"}
+			provided := map[string]any{"a": 1}
+			out, err := filterMissing(keys, provided)
+			assert.NoError(t, err)
+			assert.Empty(t, out)
+		})
+	})
+
+	t.Run("requiredFields", func(t *testing.T) {
+		t.Run("should identify required fields", func(t *testing.T) {
+			cueStr := `
+			parameter: {
+				name: string
+				age: int
+				nested: {
+					field1: string
+					field2: bool
+				}
+			}
+			`
+			var r cue.Runtime
+			inst, err := r.Compile("", cueStr)
+			assert.NoError(t, err)
+			val := inst.Value()
+			paramVal := val.LookupPath(cue.ParsePath("parameter"))
+
+			fields, err := requiredFields(paramVal)
+			assert.NoError(t, err)
+			assert.ElementsMatch(t, []string{"name", "age", "nested.field1", "nested.field2"}, fields)
+		})
+
+		t.Run("should ignore optional and default fields", func(t *testing.T) {
+			cueStr := `
+			parameter: {
+				name: string
+				age?: int
+				location: string | *"unknown"
+				nested: {
+					field1: string
+					field2?: bool
+				}
+			}
+			`
+			var r cue.Runtime
+			inst, err := r.Compile("", cueStr)
+			assert.NoError(t, err)
+			val := inst.Value()
+			paramVal := val.LookupPath(cue.ParsePath("parameter"))
+
+			fields, err := requiredFields(paramVal)
+			assert.NoError(t, err)
+			assert.ElementsMatch(t, []string{"name", "nested.field1"}, fields)
+		})
+	})
+}
+
+func TestEnforceRequiredParams(t *testing.T) {
+	var r cue.Runtime
+	cueStr := `
+		parameter: {
+			image: string
+			replicas: int
+			port: int
+			data: {
+				key: string
+				value: string
+			}
+		}
+		`
+	inst, err := r.Compile("", cueStr)
+	assert.NoError(t, err)
+	root := inst.Value()
+
+	t.Run("should pass if all params are provided directly", func(t *testing.T) {
+		params := map[string]any{
+			"image":    "nginx",
+			"replicas": 2,
+			"port":     80,
+			"data": map[string]any{
+				"key":   "k",
+				"value": "v",
+			},
+		}
+		app := &Appfile{}
+		err := enforceRequiredParams(root, params, app)
+		assert.NoError(t, err)
+	})
+
+	t.Run("should fail if params are missing", func(t *testing.T) {
+		params := map[string]any{
+			"image": "nginx",
+		}
+		app := &Appfile{}
+		err := enforceRequiredParams(root, params, app)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "missing parameters: replicas,port,data.key,data.value")
+	})
+}
+
+func TestParser_ValidateCUESchematicAppfile(t *testing.T) {
+	assert.NoError(t, utilfeature.DefaultMutableFeatureGate.Set(string(features.EnableCueValidation)+"=true"))
+	t.Cleanup(func() {
+		assert.NoError(t, utilfeature.DefaultMutableFeatureGate.Set(string(features.EnableCueValidation)+"=false"))
+	})
+
+	t.Run("should validate a valid CUE schematic appfile", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params: map[string]any{
+						"image": "nginx",
+					},
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+								spec: {
+									template: {
+										spec: {
+											containers: [{
+												name: "my-container"
+												image: parameter.image
+											}]
+										}
+									}
+								}
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+					Traits: []*Trait{
+						{
+							Name:               "my-trait",
+							CapabilityCategory: types.CUECategory,
+							Template: `
+								parameter: {
+								domain: string
+							}
+							patch: {}
+						`,
+							Params: map[string]any{
+								"domain": "example.com",
+							},
+							engine: definition.NewTraitAbstractEngine("my-trait"),
+						},
+					},
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.NoError(t, err)
+	})
+
+	t.Run("should return error for invalid trait evaluation", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params: map[string]any{
+						"image": "nginx",
+					},
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+					Traits: []*Trait{
+						{
+							Name:               "my-trait",
+							CapabilityCategory: types.CUECategory,
+							Template: `
+								// invalid CUE template
+								parameter: {
+									domain: string
+								}
+							patch: {
+									invalid: {
+							}
+						`,
+							Params: map[string]any{
+								"domain": "example.com",
+							},
+							engine: definition.NewTraitAbstractEngine("my-trait"),
+						},
+					},
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "cannot evaluate trait \"my-trait\"")
+	})
+
+	t.Run("should return error for missing parameters", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "worker",
+					CapabilityCategory: types.CUECategory,
+					Params:             map[string]any{}, // no params provided
+					FullTemplate: &Template{
+						TemplateStr: `
+							parameter: {
+								image: string
+							}
+							output: {
+								apiVersion: "apps/v1"
+								kind: "Deployment"
+							}
+						`,
+					},
+					engine: definition.NewWorkloadAbstractEngine("my-comp"),
+				},
+			},
+		}
+
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "missing parameters: image")
+	})
+
+	t.Run("should skip non-CUE components", func(t *testing.T) {
+		appfile := &Appfile{
+			Name:      "test-app",
+			Namespace: "test-ns",
+			ParsedComponents: []*Component{
+				{
+					Name:               "my-comp",
+					Type:               "helm",
+					CapabilityCategory: types.TerraformCategory,
+				},
+			},
+		}
+		p := &Parser{}
+		err := p.ValidateCUESchematicAppfile(appfile)
+		assert.NoError(t, err)
+	})
+}
diff --git a/pkg/component/main_test.go b/pkg/component/main_test.go
new file mode 100644
index 00000000..423a9769
--- /dev/null
+++ b/pkg/component/main_test.go
@@ -0,0 +1,66 @@
+/*
+Copyright 2021 The KubeVela 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 component
+
+import (
+	"log"
+	"math/rand"
+	"os"
+	"testing"
+	"time"
+
+	"k8s.io/client-go/rest"
+	"k8s.io/utils/ptr"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+
+	pkgcommon "github.com/oam-dev/kubevela/pkg/utils/common"
+)
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+
+func TestMain(m *testing.M) {
+	rand.Seed(time.Now().UnixNano())
+
+	testEnv = &envtest.Environment{
+		ControlPlaneStartTimeout: time.Minute * 3,
+		ControlPlaneStopTimeout:  time.Minute,
+		UseExistingCluster:       ptr.To(false),
+		CRDDirectoryPaths:        []string{"./testdata"},
+	}
+
+	var err error
+	cfg, err = testEnv.Start()
+	if err != nil {
+		log.Fatalf("Failed to start test environment: %v", err)
+	}
+
+	cfg.Timeout = time.Minute * 2
+	k8sClient, err = client.New(cfg, client.Options{Scheme: pkgcommon.Scheme})
+	if err != nil {
+		log.Fatalf("Failed to create new kube client: %v", err)
+	}
+
+	code := m.Run()
+
+	if err = testEnv.Stop(); err != nil {
+		log.Printf("Failed to tear down the test environment: %v", err)
+	}
+	os.Exit(code)
+}
diff --git a/pkg/component/ref_objects_suite_test.go b/pkg/component/ref_objects_suite_test.go
deleted file mode 100644
index 21d1a6a7..00000000
--- a/pkg/component/ref_objects_suite_test.go
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
-Copyright 2021 The KubeVela 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 component
-
-import (
-	"context"
-	"math/rand"
-	"testing"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	rbacv1 "k8s.io/api/rbac/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	utilfeature "k8s.io/apiserver/pkg/util/feature"
-	"k8s.io/client-go/rest"
-	featuregatetesting "k8s.io/component-base/featuregate/testing"
-	"k8s.io/utils/ptr"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-
-	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
-	"github.com/oam-dev/kubevela/pkg/features"
-	pkgcommon "github.com/oam-dev/kubevela/pkg/utils/common"
-)
-
-var cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestUtils(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "Utils Suite")
-}
-
-var _ = BeforeSuite(func() {
-	rand.Seed(time.Now().UnixNano())
-	By("bootstrapping test environment for utils test")
-
-	testEnv = &envtest.Environment{
-		ControlPlaneStartTimeout: time.Minute * 3,
-		ControlPlaneStopTimeout:  time.Minute,
-		UseExistingCluster:       ptr.To(false),
-		CRDDirectoryPaths:        []string{"./testdata"},
-	}
-
-	By("start kube test env")
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ShouldNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-
-	By("new kube client")
-	cfg.Timeout = time.Minute * 2
-	k8sClient, err = client.New(cfg, client.Options{Scheme: pkgcommon.Scheme})
-	Expect(err).Should(Succeed())
-})
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
-
-var _ = Describe("Test ref-objects functions", func() {
-	It("Test SelectRefObjectsForDispatch", func() {
-		featuregatetesting.SetFeatureGateDuringTest(GinkgoT(), utilfeature.DefaultFeatureGate, features.LegacyObjectTypeIdentifier, true)
-		featuregatetesting.SetFeatureGateDuringTest(GinkgoT(), utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, true)
-		By("Create objects")
-		Expect(k8sClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}})).Should(Succeed())
-		for _, obj := range []client.Object{&corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "dynamic",
-				Namespace: "test",
-			},
-		}, &corev1.Service{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:       "dynamic",
-				Namespace:  "test",
-				Generation: int64(5),
-			},
-			Spec: corev1.ServiceSpec{
-				ClusterIP: "10.0.0.254",
-				Ports:     []corev1.ServicePort{{Port: 80}},
-			},
-		}, &corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "by-label-1",
-				Namespace: "test",
-				Labels:    map[string]string{"key": "value"},
-			},
-		}, &corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "by-label-2",
-				Namespace: "test",
-				Labels:    map[string]string{"key": "value"},
-			},
-		}, &rbacv1.ClusterRole{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "test-cluster-role",
-			},
-		}} {
-			Expect(k8sClient.Create(context.Background(), obj)).Should(Succeed())
-		}
-		createUnstructured := func(apiVersion string, kind string, name string, namespace string, labels map[string]interface{}) *unstructured.Unstructured {
-			un := &unstructured.Unstructured{
-				Object: map[string]interface{}{
-					"apiVersion": apiVersion,
-					"kind":       kind,
-					"metadata":   map[string]interface{}{"name": name},
-				},
-			}
-			if namespace != "" {
-				un.SetNamespace(namespace)
-			}
-			if labels != nil {
-				un.Object["metadata"].(map[string]interface{})["labels"] = labels
-			}
-			return un
-		}
-		testcases := map[string]struct {
-			Input         v1alpha1.ObjectReferrer
-			compName      string
-			appNs         string
-			Output        []*unstructured.Unstructured
-			Error         string
-			Scope         string
-			IsService     bool
-			IsClusterRole bool
-		}{
-			"normal": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"legacy-type-identifier": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "v1"}},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"invalid-apiVersion": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "a/b/v1"}},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs: "test",
-				Error: "invalid APIVersion",
-			},
-			"invalid-type-identifier": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs: "test",
-				Error: "neither resource or apiVersion/kind is set",
-			},
-			"name-and-selector-both-set": {
-				Input: v1alpha1.ObjectReferrer{ObjectSelector: v1alpha1.ObjectSelector{Name: "dynamic", LabelSelector: map[string]string{"key": "value"}}},
-				appNs: "test",
-				Error: "invalid object selector for ref-objects, name and labelSelector cannot be both set",
-			},
-			"empty-ref-object-name": {
-				Input:    v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"}},
-				compName: "dynamic",
-				appNs:    "test",
-				Output:   []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cannot-find-ref-object": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "static"},
-				},
-				appNs: "test",
-				Error: "failed to load ref object",
-			},
-			"modify-service": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "service"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
-				},
-				appNs:     "test",
-				IsService: true,
-			},
-			"by-labels": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{LabelSelector: map[string]string{"key": "value"}},
-				},
-				appNs: "test",
-				Output: []*unstructured.Unstructured{
-					createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
-					createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
-				},
-			},
-			"by-deprecated-labels": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{DeprecatedLabelSelector: map[string]string{"key": "value"}},
-				},
-				appNs: "test",
-				Output: []*unstructured.Unstructured{
-					createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
-					createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
-				},
-			},
-			"no-kind-for-resource": {
-				Input: v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "unknown"}},
-				appNs: "test",
-				Error: "no matches",
-			},
-			"cross-namespace": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
-				},
-				appNs:  "demo",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cross-namespace-forbidden": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
-				},
-				appNs: "demo",
-				Scope: RefObjectsAvailableScopeNamespace,
-				Error: "cannot refer to objects outside the application's namespace",
-			},
-			"cross-cluster": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
-				},
-				appNs:  "test",
-				Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
-			},
-			"cross-cluster-forbidden": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
-				},
-				appNs: "test",
-				Scope: RefObjectsAvailableScopeCluster,
-				Error: "cannot refer to objects outside control plane",
-			},
-			"test-cluster-scope-resource": {
-				Input: v1alpha1.ObjectReferrer{
-					ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "clusterrole"},
-					ObjectSelector:       v1alpha1.ObjectSelector{Name: "test-cluster-role"},
-				},
-				appNs:         "test",
-				Scope:         RefObjectsAvailableScopeCluster,
-				Output:        []*unstructured.Unstructured{createUnstructured("rbac.authorization.k8s.io/v1", "ClusterRole", "test-cluster-role", "", nil)},
-				IsClusterRole: true,
-			},
-		}
-		for name, tt := range testcases {
-			By("Test " + name)
-			if tt.Scope == "" {
-				tt.Scope = RefObjectsAvailableScopeGlobal
-			}
-			RefObjectsAvailableScope = tt.Scope
-			output, err := SelectRefObjectsForDispatch(context.Background(), k8sClient, tt.appNs, tt.compName, tt.Input)
-			if tt.Error != "" {
-				Expect(err).ShouldNot(BeNil())
-				Expect(err.Error()).Should(ContainSubstring(tt.Error))
-			} else {
-				Expect(err).Should(Succeed())
-				if tt.IsService {
-					Expect(output[0].Object["kind"]).Should(Equal("Service"))
-					Expect(output[0].Object["spec"].(map[string]interface{})["clusterIP"]).Should(BeNil())
-				} else {
-					if tt.IsClusterRole {
-						delete(output[0].Object, "rules")
-					}
-					Expect(output).Should(Equal(tt.Output))
-				}
-			}
-		}
-	})
-
-	It("Test AppendUnstructuredObjects", func() {
-		testCases := map[string]struct {
-			Inputs  []*unstructured.Unstructured
-			Input   *unstructured.Unstructured
-			Outputs []*unstructured.Unstructured
-		}{
-			"overlap": {
-				Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}},
-				Input: &unstructured.Unstructured{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "c",
-				}},
-				Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "c",
-				}}},
-			},
-			"append": {
-				Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}},
-				Input: &unstructured.Unstructured{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
-					"data":       "c",
-				}},
-				Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
-					"data":       "a",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
-					"data":       "b",
-				}}, {Object: map[string]interface{}{
-					"apiVersion": "v1",
-					"kind":       "ConfigMap",
-					"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
-					"data":       "c",
-				}}},
-			},
-		}
-		for name, tt := range testCases {
-			By("Test " + name)
-			Expect(AppendUnstructuredObjects(tt.Inputs, tt.Input)).Should(Equal(tt.Outputs))
-		}
-	})
-
-})
diff --git a/pkg/component/ref_objects_test.go b/pkg/component/ref_objects_test.go
new file mode 100644
index 00000000..c65bf93d
--- /dev/null
+++ b/pkg/component/ref_objects_test.go
@@ -0,0 +1,748 @@
+/*
+Copyright 2021 The KubeVela 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 component
+
+import (
+	"context"
+	"encoding/json"
+	"reflect"
+	"strings"
+	"testing"
+
+	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+	featuregatetesting "k8s.io/component-base/featuregate/testing"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
+	"github.com/oam-dev/kubevela/pkg/features"
+)
+
+func TestGetLabelSelectorFromRefObjectSelector(t *testing.T) {
+	type args struct {
+		selector v1alpha1.ObjectReferrer
+	}
+	tests := []struct {
+		name      string
+		args      args
+		featureOn bool
+		want      map[string]string
+	}{
+		{
+			name: "label selector present",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			want: map[string]string{"app": "my-app"},
+		},
+		{
+			name: "deprecated label selector present and feature on",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: true,
+			want:      map[string]string{"app": "my-app-deprecated"},
+		},
+		{
+			name: "deprecated label selector present and feature off",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: false,
+			want:      nil,
+		},
+		{
+			name: "both present, label selector takes precedence",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector:           map[string]string{"app": "my-app"},
+						DeprecatedLabelSelector: map[string]string{"app": "my-app-deprecated"},
+					},
+				},
+			},
+			featureOn: true,
+			want:      map[string]string{"app": "my-app"},
+		},
+		{
+			name: "no selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{},
+			},
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, tt.featureOn)
+			if got := GetLabelSelectorFromRefObjectSelector(tt.args.selector); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("GetLabelSelectorFromRefObjectSelector() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestValidateRefObjectSelector(t *testing.T) {
+	type args struct {
+		selector v1alpha1.ObjectReferrer
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "name only",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						Name: "my-obj",
+					},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "label selector only",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "both name and label selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{
+					ObjectSelector: v1alpha1.ObjectSelector{
+						Name:          "my-obj",
+						LabelSelector: map[string]string{"app": "my-app"},
+					},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty selector",
+			args: args{
+				selector: v1alpha1.ObjectReferrer{},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := ValidateRefObjectSelector(tt.args.selector); (err != nil) != tt.wantErr {
+				t.Errorf("ValidateRefObjectSelector() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestClearRefObjectForDispatch(t *testing.T) {
+	un := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"metadata": map[string]interface{}{
+				"name":              "test-obj",
+				"namespace":         "default",
+				"resourceVersion":   "12345",
+				"generation":        int64(1),
+				"uid":               "abc-def",
+				"creationTimestamp": "2021-01-01T00:00:00Z",
+				"managedFields":     []interface{}{},
+				"ownerReferences":   []interface{}{},
+			},
+			"status": map[string]interface{}{
+				"phase": "Available",
+			},
+		},
+	}
+	ClearRefObjectForDispatch(un)
+
+	if un.GetResourceVersion() != "" {
+		t.Errorf("resourceVersion should be cleared")
+	}
+	if un.GetGeneration() != 0 {
+		t.Errorf("generation should be cleared")
+	}
+	if len(un.GetOwnerReferences()) != 0 {
+		t.Errorf("ownerReferences should be cleared")
+	}
+	if un.GetDeletionTimestamp() != nil {
+		t.Errorf("deletionTimestamp should be nil")
+	}
+	if len(un.GetManagedFields()) != 0 {
+		t.Errorf("managedFields should be cleared")
+	}
+	if un.GetUID() != "" {
+		t.Errorf("uid should be cleared")
+	}
+	if _, found, _ := unstructured.NestedFieldNoCopy(un.Object, "metadata", "creationTimestamp"); found {
+		t.Errorf("creationTimestamp should be removed")
+	}
+	if _, found, _ := unstructured.NestedFieldNoCopy(un.Object, "status"); found {
+		t.Errorf("status should be removed")
+	}
+
+	// Test for service
+	svc := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"name": "test-svc",
+			},
+			"spec": map[string]interface{}{
+				"clusterIP":  "1.2.3.4",
+				"clusterIPs": []interface{}{"1.2.3.4"},
+			},
+		},
+	}
+	ClearRefObjectForDispatch(svc)
+	if _, found, _ := unstructured.NestedString(svc.Object, "spec", "clusterIP"); found {
+		t.Errorf("service clusterIP should be removed")
+	}
+	if _, found, _ := unstructured.NestedStringSlice(svc.Object, "spec", "clusterIPs"); found {
+		t.Errorf("service clusterIPs should be removed")
+	}
+
+	// Test for service with ClusterIPNone
+	svcNone := &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "v1",
+			"kind":       "Service",
+			"metadata": map[string]interface{}{
+				"name": "test-svc-none",
+			},
+			"spec": map[string]interface{}{
+				"clusterIP": corev1.ClusterIPNone,
+			},
+		},
+	}
+	ClearRefObjectForDispatch(svcNone)
+	if ip, found, _ := unstructured.NestedString(svcNone.Object, "spec", "clusterIP"); !found || ip != corev1.ClusterIPNone {
+		t.Errorf("service with clusterIP None should not be removed")
+	}
+}
+
+func TestSelectRefObjectsForDispatch(t *testing.T) {
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LegacyObjectTypeIdentifier, true)
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeprecatedObjectLabelSelector, true)
+	t.Log("Create objects")
+	if err := k8sClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test"}}); err != nil {
+		t.Fatal(err)
+	}
+	for _, obj := range []client.Object{&corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "dynamic",
+			Namespace: "test",
+		},
+	}, &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:       "dynamic",
+			Namespace:  "test",
+			Generation: int64(5),
+		},
+		Spec: corev1.ServiceSpec{
+			ClusterIP: "10.0.0.254",
+			Ports:     []corev1.ServicePort{{Port: 80}},
+		},
+	}, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "by-label-1",
+			Namespace: "test",
+			Labels:    map[string]string{"key": "value"},
+		},
+	}, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "by-label-2",
+			Namespace: "test",
+			Labels:    map[string]string{"key": "value"},
+		},
+	}, &rbacv1.ClusterRole{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "test-cluster-role",
+		},
+	}} {
+		if err := k8sClient.Create(context.Background(), obj); err != nil {
+			t.Fatal(err)
+		}
+	}
+	createUnstructured := func(apiVersion string, kind string, name string, namespace string, labels map[string]interface{}) *unstructured.Unstructured {
+		un := &unstructured.Unstructured{
+			Object: map[string]interface{}{"apiVersion": apiVersion,
+				"kind":     kind,
+				"metadata": map[string]interface{}{"name": name},
+			},
+		}
+		if namespace != "" {
+			un.SetNamespace(namespace)
+		}
+		if labels != nil {
+			un.Object["metadata"].(map[string]interface{})["labels"] = labels
+		}
+		return un
+	}
+	testcases := map[string]struct {
+		Input         v1alpha1.ObjectReferrer
+		compName      string
+		appNs         string
+		Output        []*unstructured.Unstructured
+		Error         string
+		Scope         string
+		IsService     bool
+		IsClusterRole bool
+	}{
+		"normal": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"legacy-type-identifier": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "v1"}},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"invalid-apiVersion": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{LegacyObjectTypeIdentifier: v1alpha1.LegacyObjectTypeIdentifier{Kind: "ConfigMap", APIVersion: "a/b/v1"}},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs: "test",
+			Error: "invalid APIVersion",
+		},
+		"invalid-type-identifier": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs: "test",
+			Error: "neither resource or apiVersion/kind is set",
+		},
+		"name-and-selector-both-set": {
+			Input: v1alpha1.ObjectReferrer{ObjectSelector: v1alpha1.ObjectSelector{Name: "dynamic", LabelSelector: map[string]string{"key": "value"}}},
+			appNs: "test",
+			Error: "invalid object selector for ref-objects, name and labelSelector cannot be both set",
+		},
+		"empty-ref-object-name": {
+			Input:    v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"}},
+			compName: "dynamic",
+			appNs:    "test",
+			Output:   []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cannot-find-ref-object": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "static"},
+			},
+			appNs: "test",
+			Error: "failed to load ref object",
+		},
+		"modify-service": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "service"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic"},
+			},
+			appNs:     "test",
+			IsService: true,
+		},
+		"by-labels": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{LabelSelector: map[string]string{"key": "value"}},
+			},
+			appNs: "test",
+			Output: []*unstructured.Unstructured{
+				createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
+				createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
+			},
+		},
+		"by-deprecated-labels": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{DeprecatedLabelSelector: map[string]string{"key": "value"}},
+			},
+			appNs: "test",
+			Output: []*unstructured.Unstructured{
+				createUnstructured("v1", "ConfigMap", "by-label-1", "test", map[string]interface{}{"key": "value"}),
+				createUnstructured("v1", "ConfigMap", "by-label-2", "test", map[string]interface{}{"key": "value"}),
+			},
+		},
+		"no-kind-for-resource": {
+			Input: v1alpha1.ObjectReferrer{ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "unknown"}},
+			appNs: "test",
+			Error: "no matches",
+		},
+		"cross-namespace": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
+			},
+			appNs:  "demo",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cross-namespace-forbidden": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Namespace: "test"},
+			},
+			appNs: "demo",
+			Scope: RefObjectsAvailableScopeNamespace,
+			Error: "cannot refer to objects outside the application's namespace",
+		},
+		"cross-cluster": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
+			},
+			appNs:  "test",
+			Output: []*unstructured.Unstructured{createUnstructured("v1", "ConfigMap", "dynamic", "test", nil)},
+		},
+		"cross-cluster-forbidden": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "configmap"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "dynamic", Cluster: "demo"},
+			},
+			appNs: "test",
+			Scope: RefObjectsAvailableScopeCluster,
+			Error: "cannot refer to objects outside control plane",
+		},
+		"test-cluster-scope-resource": {
+			Input: v1alpha1.ObjectReferrer{
+				ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "clusterrole"},
+				ObjectSelector:       v1alpha1.ObjectSelector{Name: "test-cluster-role"},
+			},
+			appNs:         "test",
+			Scope:         RefObjectsAvailableScopeCluster,
+			Output:        []*unstructured.Unstructured{createUnstructured("rbac.authorization.k8s.io/v1", "ClusterRole", "test-cluster-role", "", nil)},
+			IsClusterRole: true,
+		},
+	}
+	for name, tt := range testcases {
+		t.Run(name, func(t *testing.T) {
+			if tt.Scope == "" {
+				tt.Scope = RefObjectsAvailableScopeGlobal
+			}
+			RefObjectsAvailableScope = tt.Scope
+			output, err := SelectRefObjectsForDispatch(context.Background(), k8sClient, tt.appNs, tt.compName, tt.Input)
+			if tt.Error != "" {
+				if err == nil {
+					t.Fatalf("expected error, got nil")
+				}
+				if !strings.Contains(err.Error(), tt.Error) {
+					t.Fatalf("expected error message to contain %q, got %q", tt.Error, err.Error())
+				}
+			} else {
+				if err != nil {
+					t.Fatal(err)
+				}
+				if tt.IsService {
+					if output[0].Object["kind"] != "Service" {
+						t.Fatalf(`expected kind to be "Service", got %q`, output[0].Object["kind"])
+					}
+					if output[0].Object["spec"].(map[string]interface{})["clusterIP"] != nil {
+						t.Fatalf(`expected clusterIP to be nil, got %v`, output[0].Object["spec"].(map[string]interface{})["clusterIP"])
+					}
+				} else {
+					if tt.IsClusterRole {
+						delete(output[0].Object, "rules")
+					}
+					if !reflect.DeepEqual(output, tt.Output) {
+						t.Fatalf("expected output to be %v, got %v", tt.Output, output)
+					}
+				}
+			}
+		})
+	}
+}
+
+func TestReferredObjectsDelegatingClient(t *testing.T) {
+	objs := []*unstructured.Unstructured{
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name":      "cm-1",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-1"},
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name":      "cm-2",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-2"},
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "Secret",
+				"metadata": map[string]interface{}{
+					"name":      "secret-1",
+					"namespace": "ns-1",
+					"labels":    map[string]interface{}{"app": "app-1"},
+				},
+			},
+		},
+	}
+
+	delegatingClient := ReferredObjectsDelegatingClient(k8sClient, objs)
+
+	t.Run("Get", func(t *testing.T) {
+		t.Run("should get existing object", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-1", Namespace: "ns-1"}, obj)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if obj.GetName() != "cm-1" {
+				t.Errorf("expected object name cm-1, got %s", obj.GetName())
+			}
+		})
+
+		t.Run("should return not found for non-existing object", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-non-existent", Namespace: "ns-1"}, obj)
+			if !apierrors.IsNotFound(err) {
+				t.Errorf("expected not found error, got %v", err)
+			}
+		})
+
+		t.Run("should return not found for different kind", func(t *testing.T) {
+			obj := &unstructured.Unstructured{}
+			obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"})
+			err := delegatingClient.Get(context.Background(), client.ObjectKey{Name: "cm-1", Namespace: "ns-1"}, obj)
+			if !apierrors.IsNotFound(err) {
+				t.Errorf("expected not found error, got %v", err)
+			}
+		})
+	})
+
+	t.Run("List", func(t *testing.T) {
+		t.Run("should list all objects of a kind", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 2 {
+				t.Errorf("expected 2 items, got %d", len(list.Items))
+			}
+		})
+
+		t.Run("should list with namespace", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list, client.InNamespace("ns-1"))
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 2 {
+				t.Errorf("expected 2 items, got %d", len(list.Items))
+			}
+
+			list.Items = []unstructured.Unstructured{}
+			err = delegatingClient.List(context.Background(), list, client.InNamespace("ns-2"))
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 0 {
+				t.Errorf("expected 0 items, got %d", len(list.Items))
+			}
+		})
+
+		t.Run("should list with label selector", func(t *testing.T) {
+			list := &unstructured.UnstructuredList{}
+			list.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMapList"})
+			err := delegatingClient.List(context.Background(), list, client.MatchingLabels{"app": "app-1"})
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			if len(list.Items) != 1 {
+				t.Errorf("expected 1 item, got %d", len(list.Items))
+			}
+			if list.Items[0].GetName() != "cm-1" {
+				t.Errorf("expected cm-1, got %s", list.Items[0].GetName())
+			}
+		})
+	})
+}
+
+func TestAppendUnstructuredObjects(t *testing.T) {
+	testCases := map[string]struct {
+		Inputs  []*unstructured.Unstructured
+		Input   *unstructured.Unstructured
+		Outputs []*unstructured.Unstructured
+	}{
+		"overlap": {
+			Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}},
+			Input: &unstructured.Unstructured{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "c",
+			}},
+			Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "c",
+			}}},
+		},
+		"append": {
+			Inputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}},
+			Input: &unstructured.Unstructured{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
+				"data":       "c",
+			}},
+			Outputs: []*unstructured.Unstructured{{Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "x", "namespace": "default"},
+				"data":       "a",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "y", "namespace": "default"},
+				"data":       "b",
+			}}, {Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata":   map[string]interface{}{"name": "z", "namespace": "default"},
+				"data":       "c",
+			}}},
+		},
+	}
+	for name, tt := range testCases {
+		t.Run(name, func(t *testing.T) {
+			got := AppendUnstructuredObjects(tt.Inputs, tt.Input)
+			if !reflect.DeepEqual(got, tt.Outputs) {
+				t.Fatalf("expected output to be %v, got %v", tt.Outputs, got)
+			}
+		})
+	}
+}
+
+func TestConvertUnstructuredsToReferredObjects(t *testing.T) {
+	uns := []*unstructured.Unstructured{
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "v1",
+				"kind":       "ConfigMap",
+				"metadata": map[string]interface{}{
+					"name": "cm-1",
+				},
+			},
+		},
+		{
+			Object: map[string]interface{}{
+				"apiVersion": "apps/v1",
+				"kind":       "Deployment",
+				"metadata": map[string]interface{}{
+					"name": "deploy-1",
+				},
+			},
+		},
+	}
+
+	refObjs, err := ConvertUnstructuredsToReferredObjects(uns)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+
+	if len(refObjs) != 2 {
+		t.Fatalf("expected 2 referred objects, got %d", len(refObjs))
+	}
+
+	for i, un := range uns {
+		raw, err := json.Marshal(un)
+		if err != nil {
+			t.Fatalf("failed to marshal unstructured: %v", err)
+		}
+		expected := common.ReferredObject{
+			RawExtension: runtime.RawExtension{Raw: raw},
+		}
+		if !reflect.DeepEqual(refObjs[i], expected) {
+			t.Errorf("expected refObj %v, got %v", expected, refObjs[i])
+		}
+	}
+}
diff --git a/pkg/controller/core.oam.dev/v1beta1/application/generator_test.go b/pkg/controller/core.oam.dev/v1beta1/application/generator_test.go
index 06a38a85..7314c6dc 100644
--- a/pkg/controller/core.oam.dev/v1beta1/application/generator_test.go
+++ b/pkg/controller/core.oam.dev/v1beta1/application/generator_test.go
@@ -289,4 +289,31 @@ var _ = Describe("Test Application workflow generator", func() {
 		_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af)
 		Expect(err).NotTo(BeNil())
 	})
+
+	It("Test workflow context contains app labels and annotations", func() {
+		app := &oamcore.Application{
+			TypeMeta: metav1.TypeMeta{Kind: "Application", APIVersion: "core.oam.dev/v1beta1"},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:        "app-with-meta",
+				Namespace:   namespaceName,
+				Labels:      map[string]string{"team": "platform", "env": "prod"},
+				Annotations: map[string]string{"description": "meta test", "owner": "sre"},
+			},
+			Spec: oamcore.ApplicationSpec{Components: []common.ApplicationComponent{}},
+		}
+		ctxData := generateContextDataFromApp(app, "apprev-with-meta")
+		Expect(ctxData.AppLabels).To(Equal(app.Labels))
+		Expect(ctxData.AppAnnotations).To(Equal(app.Annotations))
+	})
+
+	It("Test workflow context empty labels annotations", func() {
+		app := &oamcore.Application{
+			TypeMeta:   metav1.TypeMeta{Kind: "Application", APIVersion: "core.oam.dev/v1beta1"},
+			ObjectMeta: metav1.ObjectMeta{Name: "app-without-meta", Namespace: namespaceName},
+			Spec:       oamcore.ApplicationSpec{Components: []common.ApplicationComponent{}},
+		}
+		ctxData := generateContextDataFromApp(app, "apprev-without-meta")
+		Expect(ctxData.AppLabels).To(BeNil())
+		Expect(ctxData.AppAnnotations).To(BeNil())
+	})
 })
diff --git a/pkg/cue/definition/template_test.go b/pkg/cue/definition/template_test.go
index 15a9f5bf..dfd5965d 100644
--- a/pkg/cue/definition/template_test.go
+++ b/pkg/cue/definition/template_test.go
@@ -725,7 +725,7 @@ parameter: {
         }
       }
       outputs: ingress: {
-        apiVersion: "networking.k8s.io/v1beta1"
+        apiVersion: "networking.k8s.io/v1"
         kind: "Ingress"
         metadata:
           name: context.name
@@ -736,9 +736,14 @@ parameter: {
             http: {
               paths: [{
                   path: parameter.path
+                  pathType: "Prefix"
                   backend: {
-                    serviceName: context.name
-                    servicePort: parameter.exposePort
+                    service: {
+                      name: context.name
+                      port: {
+                        number: parameter.exposePort
+                      }
+                    }
                   }
               }]
             }
@@ -781,7 +786,7 @@ parameter: {
 						"metadata":   map[string]interface{}{"name": "testgame-config"}, "data": map[string]interface{}{"enemies": "enemies-data", "lives": "lives-data"}},
 				},
 				"t3service": &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "v1", "kind": "Service", "spec": map[string]interface{}{"ports": []interface{}{map[string]interface{}{"port": int64(1080), "targetPort": int64(443)}}, "selector": map[string]interface{}{"app": "test"}}}},
-				"t3ingress": &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "networking.k8s.io/v1beta1", "kind": "Ingress", "labels": map[string]interface{}{"config": "enemies-data"}, "metadata": map[string]interface{}{"name": "test"}, "spec": map[string]interface{}{"rules": []interface{}{map[string]interface{}{"host": "example.com", "http": map[string]interface{}{"paths": []interface{}{map[string]interface{}{"backend": map[string]interface{}{"serviceName": "test", "servicePort": int64(1080)}, "path": "ping"}}}}}}}},
+				"t3ingress": &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "networking.k8s.io/v1", "kind": "Ingress", "labels": map[string]interface{}{"config": "enemies-data"}, "metadata": map[string]interface{}{"name": "test"}, "spec": map[string]interface{}{"rules": []interface{}{map[string]interface{}{"host": "example.com", "http": map[string]interface{}{"paths": []interface{}{map[string]interface{}{"path": "ping", "pathType": "Prefix", "backend": map[string]interface{}{"service": map[string]interface{}{"name": "test", "port": map[string]interface{}{"number": int64(1080)}}}}}}}}}}},
 			},
 		},
 		"outputs trait with schema": {
diff --git a/pkg/webhook/core.oam.dev/v1beta1/application/validation_handlers_test.go b/pkg/webhook/core.oam.dev/v1beta1/application/validation_handlers_test.go
new file mode 100644
index 00000000..4b926aa7
--- /dev/null
+++ b/pkg/webhook/core.oam.dev/v1beta1/application/validation_handlers_test.go
@@ -0,0 +1,418 @@
+/*
+Copyright 2025 The KubeVela 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 application
+
+import (
+	"context"
+	"testing"
+
+	"github.com/kubevela/pkg/controller/sharding"
+	"github.com/stretchr/testify/assert"
+	admissionv1 "k8s.io/api/admission/v1"
+	authenticationv1 "k8s.io/api/authentication/v1"
+	authv1 "k8s.io/api/authorization/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+	featuregatetesting "k8s.io/component-base/featuregate/testing"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/features"
+	"github.com/oam-dev/kubevela/pkg/oam"
+)
+
+func TestValidateCreate(t *testing.T) {
+	// Disable the definition validation feature for this test
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, features.ValidateDefinitionPermissions, false)
+
+	// Enable sharding to skip component validation (since we don't have component definitions in tests)
+	oldSharding := sharding.EnableSharding
+	sharding.EnableSharding = true
+	defer func() { sharding.EnableSharding = oldSharding }()
+
+	scheme := runtime.NewScheme()
+	_ = v1beta1.AddToScheme(scheme)
+	_ = authv1.AddToScheme(scheme)
+
+	testCases := []struct {
+		name               string
+		app                *v1beta1.Application
+		expectedErrorCount int
+		expectedErrorField string
+	}{
+		{
+			name: "valid application",
+			app: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+					},
+				},
+			},
+			expectedErrorCount: 0,
+		},
+		{
+			name: "application with both autoUpdate and publishVersion",
+			app: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+					Annotations: map[string]string{
+						oam.AnnotationAutoUpdate:     "true",
+						oam.AnnotationPublishVersion: "v1.0.0",
+					},
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+					},
+				},
+			},
+			expectedErrorCount: 1,
+			expectedErrorField: "metadata.annotations",
+		},
+		{
+			name: "application with duplicate workflow step names",
+			app: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+					},
+					Workflow: &v1beta1.Workflow{
+						Steps: []workflowv1alpha1.WorkflowStep{
+							{
+								WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
+									Name: "step1",
+									Type: "deploy",
+								},
+							},
+							{
+								WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
+									Name: "step1", // Duplicate name
+									Type: "deploy",
+								},
+							},
+						},
+					},
+				},
+			},
+			expectedErrorCount: 1,
+			expectedErrorField: "spec.workflow.steps",
+		},
+		{
+			name: "application with invalid timeout",
+			app: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+					},
+					Workflow: &v1beta1.Workflow{
+						Steps: []workflowv1alpha1.WorkflowStep{
+							{
+								WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
+									Name:    "step1",
+									Type:    "deploy",
+									Timeout: "invalid",
+								},
+							},
+						},
+					},
+				},
+			},
+			expectedErrorCount: 1,
+			expectedErrorField: "spec.workflow.steps.timeout",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			handler := &ValidatingHandler{
+				Client: fake.NewClientBuilder().WithScheme(scheme).Build(),
+			}
+
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					UserInfo: authenticationv1.UserInfo{
+						Username: "test-user",
+						Groups:   []string{"test-group"},
+					},
+				},
+			}
+
+			errs := handler.ValidateCreate(context.Background(), tc.app, req)
+			assert.Equal(t, tc.expectedErrorCount, len(errs),
+				"Expected %d errors, got %d: %v", tc.expectedErrorCount, len(errs), errs)
+
+			if tc.expectedErrorField != "" && len(errs) > 0 {
+				assert.Contains(t, errs[0].Field, tc.expectedErrorField,
+					"Expected error field to contain %s, got %s", tc.expectedErrorField, errs[0].Field)
+			}
+		})
+	}
+}
+
+func TestValidateUpdate(t *testing.T) {
+	// Disable the definition validation feature for this test
+	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, features.ValidateDefinitionPermissions, false)
+
+	// Enable sharding to skip component validation (since we don't have component definitions in tests)
+	oldSharding := sharding.EnableSharding
+	sharding.EnableSharding = true
+	defer func() { sharding.EnableSharding = oldSharding }()
+
+	scheme := runtime.NewScheme()
+	_ = v1beta1.AddToScheme(scheme)
+	_ = authv1.AddToScheme(scheme)
+
+	handler := &ValidatingHandler{
+		Client: fake.NewClientBuilder().WithScheme(scheme).Build(),
+	}
+
+	oldApp := &v1beta1.Application{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-app",
+			Namespace: "default",
+		},
+		Spec: v1beta1.ApplicationSpec{
+			Components: []common.ApplicationComponent{
+				{
+					Name: "comp1",
+					Type: "webservice",
+				},
+			},
+		},
+	}
+
+	testCases := []struct {
+		name               string
+		newApp             *v1beta1.Application
+		expectedErrorCount int
+	}{
+		{
+			name: "valid update",
+			newApp: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+						{
+							Name: "comp2",
+							Type: "worker",
+						},
+					},
+				},
+			},
+			expectedErrorCount: 0,
+		},
+		{
+			name: "update with invalid annotations",
+			newApp: &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-app",
+					Namespace: "default",
+					Annotations: map[string]string{
+						oam.AnnotationAutoUpdate:     "true",
+						oam.AnnotationPublishVersion: "v1.0.0",
+					},
+				},
+				Spec: v1beta1.ApplicationSpec{
+					Components: []common.ApplicationComponent{
+						{
+							Name: "comp1",
+							Type: "webservice",
+						},
+					},
+				},
+			},
+			expectedErrorCount: 1,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					UserInfo: authenticationv1.UserInfo{
+						Username: "test-user",
+						Groups:   []string{"test-group"},
+					},
+				},
+			}
+
+			errs := handler.ValidateUpdate(context.Background(), tc.newApp, oldApp, req)
+			assert.Equal(t, tc.expectedErrorCount, len(errs),
+				"Expected %d errors, got %d: %v", tc.expectedErrorCount, len(errs), errs)
+		})
+	}
+}
+
+func TestValidateTimeout(t *testing.T) {
+	handler := &ValidatingHandler{}
+
+	testCases := []struct {
+		name          string
+		timeout       string
+		expectedError bool
+	}{
+		{
+			name:          "valid duration - seconds",
+			timeout:       "30s",
+			expectedError: false,
+		},
+		{
+			name:          "valid duration - minutes",
+			timeout:       "5m",
+			expectedError: false,
+		},
+		{
+			name:          "valid duration - hours",
+			timeout:       "2h",
+			expectedError: false,
+		},
+		{
+			name:          "valid duration - complex",
+			timeout:       "1h30m45s",
+			expectedError: false,
+		},
+		{
+			name:          "invalid duration",
+			timeout:       "invalid",
+			expectedError: true,
+		},
+		{
+			name:          "invalid duration - missing unit",
+			timeout:       "30",
+			expectedError: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			errs := handler.ValidateTimeout("test-step", tc.timeout)
+			if tc.expectedError {
+				assert.NotEmpty(t, errs, "Expected validation error for timeout %s", tc.timeout)
+			} else {
+				assert.Empty(t, errs, "Expected no validation error for timeout %s", tc.timeout)
+			}
+		})
+	}
+}
+
+func TestValidateAnnotations(t *testing.T) {
+	handler := &ValidatingHandler{}
+
+	testCases := []struct {
+		name               string
+		annotations        map[string]string
+		expectedErrorCount int
+	}{
+		{
+			name:               "no annotations",
+			annotations:        nil,
+			expectedErrorCount: 0,
+		},
+		{
+			name: "only autoUpdate",
+			annotations: map[string]string{
+				oam.AnnotationAutoUpdate: "true",
+			},
+			expectedErrorCount: 0,
+		},
+		{
+			name: "only publishVersion",
+			annotations: map[string]string{
+				oam.AnnotationPublishVersion: "v1.0.0",
+			},
+			expectedErrorCount: 0,
+		},
+		{
+			name: "both autoUpdate and publishVersion",
+			annotations: map[string]string{
+				oam.AnnotationAutoUpdate:     "true",
+				oam.AnnotationPublishVersion: "v1.0.0",
+			},
+			expectedErrorCount: 1,
+		},
+		{
+			name: "autoUpdate false with publishVersion",
+			annotations: map[string]string{
+				oam.AnnotationAutoUpdate:     "false",
+				oam.AnnotationPublishVersion: "v1.0.0",
+			},
+			expectedErrorCount: 0,
+		},
+		{
+			name: "autoUpdate true with empty publishVersion",
+			annotations: map[string]string{
+				oam.AnnotationAutoUpdate:     "true",
+				oam.AnnotationPublishVersion: "",
+			},
+			expectedErrorCount: 0,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			app := &v1beta1.Application{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:        "test-app",
+					Namespace:   "default",
+					Annotations: tc.annotations,
+				},
+			}
+
+			errs := handler.ValidateAnnotations(context.Background(), app)
+			assert.Equal(t, tc.expectedErrorCount, len(errs),
+				"Expected %d errors, got %d: %v", tc.expectedErrorCount, len(errs), errs)
+		})
+	}
+}
diff --git a/pkg/webhook/core.oam.dev/v1beta1/application/validation_permissions_test.go b/pkg/webhook/core.oam.dev/v1beta1/application/validation_permissions_test.go
index 88aa8ade..a50ca153 100644
--- a/pkg/webhook/core.oam.dev/v1beta1/application/validation_permissions_test.go
+++ b/pkg/webhook/core.oam.dev/v1beta1/application/validation_permissions_test.go
@@ -685,8 +685,21 @@ func TestValidateDefinitionPermissions_AuthenticationDisabled(t *testing.T) {
 		},
 	}
 
+	// Use mock client that allows the definition
+	scheme := runtime.NewScheme()
+	_ = v1beta1.AddToScheme(scheme)
+	_ = authv1.AddToScheme(scheme)
+
+	fakeClient := &mockSARClient{
+		Client: fake.NewClientBuilder().WithScheme(scheme).Build(),
+		allowedDefinitions: map[string]bool{
+			"componentdefinitions/vela-system/webservice": true,
+			"componentdefinitions/test-ns/webservice":     true,
+		},
+	}
+
 	handler := &ValidatingHandler{
-		Client: fake.NewClientBuilder().Build(),
+		Client: fakeClient,
 	}
 
 	req := admission.Request{
@@ -697,9 +710,10 @@ func TestValidateDefinitionPermissions_AuthenticationDisabled(t *testing.T) {
 		},
 	}
 
-	// Should return no errors when AuthenticationWithUser is disabled
+	// Definition validation should still work even when AuthenticationWithUser is disabled
+	// It validates based on the user info in the request regardless of auth flag
 	errs := handler.ValidateDefinitionPermissions(context.Background(), app, req)
-	assert.Empty(t, errs, "Expected no errors when AuthenticationWithUser is disabled")
+	assert.Empty(t, errs, "Expected no errors when user has permission even with AuthenticationWithUser disabled")
 }
 
 func TestCollectDefinitionUsage(t *testing.T) {
diff --git a/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler_test.go b/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler_test.go
similarity index 64%
rename from pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler_test.go
rename to pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler_test.go
index 3e1c9578..4e57758c 100644
--- a/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/validating_handler_test.go
+++ b/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/component_definition_validating_handler_test.go
@@ -30,6 +30,7 @@ import (
 	admissionv1 "k8s.io/api/admission/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/rest"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -39,6 +40,7 @@ import (
 	core "github.com/oam-dev/kubevela/apis/core.oam.dev"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/features"
 )
 
 var handler ValidatingHandler
@@ -153,7 +155,7 @@ var _ = Describe("Test ComponentDefinition validating handler", func() {
 			resp := handler.Handle(context.TODO(), req)
 			Expect(resp.Allowed).Should(BeFalse())
 			Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason(http.StatusText(http.StatusForbidden))))
-			Expect(resp.Result.Message).Should(Equal("neither the type nor the definition of the workload field in the ComponentDefinition wrongCd can be empty"))
+			Expect(resp.Result.Message).Should(ContainSubstring("neither the type nor the definition of the workload field in the ComponentDefinition wrongCd can be empty"))
 		})
 
 		It("Test componentDefinition which type and definition point to different workload type", func() {
@@ -176,7 +178,7 @@ var _ = Describe("Test ComponentDefinition validating handler", func() {
 			resp := handler.Handle(context.TODO(), req)
 			Expect(resp.Allowed).Should(BeFalse())
 			Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason(http.StatusText(http.StatusForbidden))))
-			Expect(resp.Result.Message).Should(Equal("the type and the definition of the workload field in ComponentDefinition wrongCd should represent the same workload"))
+			Expect(resp.Result.Message).Should(ContainSubstring("the type and the definition of the workload field in ComponentDefinition wrongCd should represent the same workload"))
 		})
 		It("Test cue template validation passed", func() {
 			cd.Spec = v1beta1.ComponentDefinitionSpec{
@@ -401,5 +403,278 @@ var _ = Describe("Test ComponentDefinition validating handler", func() {
 			Expect(resp.Allowed).Should(BeTrue())
 		})
 
+		It("Test ComponentDefinition with non-existent CRD in outputs should be rejected", func() {
+			// Enable the ValidateResourcesExist feature gate for this test
+			originalState := utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateResourcesExist)
+			defer utilfeature.DefaultMutableFeatureGate.SetFromMap(map[string]bool{
+				string(features.ValidateResourcesExist): originalState,
+			})
+			utilfeature.DefaultMutableFeatureGate.SetFromMap(map[string]bool{
+				string(features.ValidateResourcesExist): true,
+			})
+
+			templateWithInvalidCRD := `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	invalidResource: {
+		apiVersion: "custom.io/v1alpha1"
+		kind: "NonExistentResource"
+		metadata: name: parameter.name + "-custom"
+		spec: {
+			foo: "bar"
+		}
+	}
+}`
+
+			cd := v1beta1.ComponentDefinition{}
+			cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
+			cd.SetName("test-invalid-outputs")
+			cd.Spec = v1beta1.ComponentDefinitionSpec{
+				Workload: common.WorkloadTypeDescriptor{
+					Type: "deployments.apps",
+					Definition: common.WorkloadGVK{
+						APIVersion: "apps/v1",
+						Kind:       "Deployment",
+					},
+				},
+				Schematic: &common.Schematic{
+					CUE: &common.CUE{
+						Template: templateWithInvalidCRD,
+					},
+				},
+			}
+			cdRaw, _ := json.Marshal(cd)
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					Operation: admissionv1.Create,
+					Resource:  reqResource,
+					Object:    runtime.RawExtension{Raw: cdRaw},
+				},
+			}
+			resp := handler.Handle(context.TODO(), req)
+			Expect(resp.Allowed).Should(BeFalse())
+			Expect(resp.Result.Message).Should(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Test ComponentDefinition with valid outputs should pass", func() {
+			templateWithValidOutputs := `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{
+				port: 80
+				targetPort: 8080
+			}]
+		}
+	}
+	configmap: {
+		apiVersion: "v1"
+		kind: "ConfigMap"
+		metadata: name: parameter.name + "-config"
+		data: {
+			config: "test-config"
+		}
+	}
+}`
+
+			cd := v1beta1.ComponentDefinition{}
+			cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
+			cd.SetName("test-valid-outputs")
+			cd.Spec = v1beta1.ComponentDefinitionSpec{
+				Workload: common.WorkloadTypeDescriptor{
+					Type: "deployments.apps",
+					Definition: common.WorkloadGVK{
+						APIVersion: "apps/v1",
+						Kind:       "Deployment",
+					},
+				},
+				Schematic: &common.Schematic{
+					CUE: &common.CUE{
+						Template: templateWithValidOutputs,
+					},
+				},
+			}
+			cdRaw, _ := json.Marshal(cd)
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					Operation: admissionv1.Create,
+					Resource:  reqResource,
+					Object:    runtime.RawExtension{Raw: cdRaw},
+				},
+			}
+			resp := handler.Handle(context.TODO(), req)
+			Expect(resp.Allowed).Should(BeTrue())
+		})
+
+		It("Test ComponentDefinition with mixed valid and non-k8s outputs should pass", func() {
+			templateWithMixedOutputs := `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{port: 80}]
+		}
+	}
+	customData: {
+		field1: "value1"
+		field2: "value2"
+		nested: {
+			data: "some-data"
+		}
+	}
+}`
+
+			cd := v1beta1.ComponentDefinition{}
+			cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
+			cd.SetName("test-mixed-outputs")
+			cd.Spec = v1beta1.ComponentDefinitionSpec{
+				Workload: common.WorkloadTypeDescriptor{
+					Type: "deployments.apps",
+					Definition: common.WorkloadGVK{
+						APIVersion: "apps/v1",
+						Kind:       "Deployment",
+					},
+				},
+				Schematic: &common.Schematic{
+					CUE: &common.CUE{
+						Template: templateWithMixedOutputs,
+					},
+				},
+			}
+			cdRaw, _ := json.Marshal(cd)
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					Operation: admissionv1.Create,
+					Resource:  reqResource,
+					Object:    runtime.RawExtension{Raw: cdRaw},
+				},
+			}
+			resp := handler.Handle(context.TODO(), req)
+			Expect(resp.Allowed).Should(BeTrue())
+		})
+
+		It("Test ComponentDefinition with empty outputs should pass", func() {
+			templateWithEmptyOutputs := `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {}`
+
+			cd := v1beta1.ComponentDefinition{}
+			cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
+			cd.SetName("test-empty-outputs")
+			cd.Spec = v1beta1.ComponentDefinitionSpec{
+				Workload: common.WorkloadTypeDescriptor{
+					Type: "deployments.apps",
+					Definition: common.WorkloadGVK{
+						APIVersion: "apps/v1",
+						Kind:       "Deployment",
+					},
+				},
+				Schematic: &common.Schematic{
+					CUE: &common.CUE{
+						Template: templateWithEmptyOutputs,
+					},
+				},
+			}
+			cdRaw, _ := json.Marshal(cd)
+			req := admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					Operation: admissionv1.Create,
+					Resource:  reqResource,
+					Object:    runtime.RawExtension{Raw: cdRaw},
+				},
+			}
+			resp := handler.Handle(context.TODO(), req)
+			Expect(resp.Allowed).Should(BeTrue())
+		})
+
 	})
 })
diff --git a/pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler_test.go b/pkg/webhook/core.oam.dev/v1beta1/policydefinition/policy_definition_validating_handler_test.go
similarity index 100%
rename from pkg/webhook/core.oam.dev/v1beta1/policydefinition/validating_handler_test.go
rename to pkg/webhook/core.oam.dev/v1beta1/policydefinition/policy_definition_validating_handler_test.go
diff --git a/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler_test.go b/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler_test.go
similarity index 99%
rename from pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler_test.go
rename to pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler_test.go
index 9fdf5899..ec8675e9 100644
--- a/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/validating_handler_test.go
+++ b/pkg/webhook/core.oam.dev/v1beta1/traitdefinition/trait_definition_validating_handler_test.go
@@ -168,7 +168,7 @@ var _ = Describe("Test TraitDefinition validating handler", func() {
 			resp := handler.Handle(context.TODO(), req)
 			Expect(resp.Allowed).Should(BeFalse())
 			Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason(http.StatusText(http.StatusForbidden))))
-			Expect(resp.Result.Message).Should(Equal("mock validator error"))
+			Expect(resp.Result.Message).Should(ContainSubstring("mock validator error"))
 		})
 		It("Test cue template validation passed", func() {
 			td.Spec = v1beta1.TraitDefinitionSpec{
diff --git a/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/validating_handler_test.go b/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler_test.go
similarity index 100%
rename from pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/validating_handler_test.go
rename to pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition/workflowstep_validating_handler_test.go
diff --git a/pkg/webhook/utils/invalid_resource_check_test.go b/pkg/webhook/utils/invalid_resource_check_test.go
new file mode 100644
index 00000000..c2f9d9bd
--- /dev/null
+++ b/pkg/webhook/utils/invalid_resource_check_test.go
@@ -0,0 +1,307 @@
+/*
+ Copyright 2021. The KubeVela 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 utils
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+
+	"github.com/oam-dev/kubevela/pkg/features"
+)
+
+// fakeRESTMapper implements meta.RESTMapper for tests (minimal).
+type fakeRESTMapper struct {
+	known map[string]bool // key: group|kind|version
+}
+
+func newFakeRESTMapper(entries ...[3]string) *fakeRESTMapper {
+	m := &fakeRESTMapper{known: map[string]bool{}}
+	for _, e := range entries {
+		m.known[e[0]+"|"+e[1]+"|"+e[2]] = true
+	}
+	return m
+}
+
+func (f *fakeRESTMapper) key(gk schema.GroupKind, version string) string {
+	return gk.Group + "|" + gk.Kind + "|" + version
+}
+
+func (f *fakeRESTMapper) Reset() {}
+func (f *fakeRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+	return schema.GroupVersionKind{}, errors.New("not implemented")
+}
+func (f *fakeRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+	return nil, errors.New("not implemented")
+}
+func (f *fakeRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+	return schema.GroupVersionResource{}, errors.New("not implemented")
+}
+func (f *fakeRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+	return nil, errors.New("not implemented")
+}
+func (f *fakeRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
+	for _, v := range versions {
+		if f.known[f.key(gk, v)] {
+			return &meta.RESTMapping{
+				Resource: schema.GroupVersionResource{
+					Group:    gk.Group,
+					Version:  v,
+					Resource: "",
+				},
+				GroupVersionKind: schema.GroupVersionKind{
+					Group:   gk.Group,
+					Version: v,
+					Kind:    gk.Kind,
+				},
+			}, nil
+		}
+	}
+	return nil, errors.New("no match for kind")
+}
+func (f *fakeRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
+	m, err := f.RESTMapping(gk, versions...)
+	if err != nil {
+		return nil, err
+	}
+	return []*meta.RESTMapping{m}, nil
+}
+func (f *fakeRESTMapper) ResourceSingularizer(resource string) (string, error) {
+	return resource, nil
+}
+
+func TestExtractResourceInfo(t *testing.T) {
+	tests := map[string]struct {
+		cue     string
+		want    []ResourceInfo
+		wantErr bool
+	}{
+		"singleOutput": {
+			cue: `
+output: {
+  apiVersion: "apps/v1"
+  kind: "Deployment"
+  metadata: { name: "my-deploy" }
+  spec: {}
+}`,
+			want: []ResourceInfo{
+				{APIVersion: "apps/v1", Kind: "Deployment", Name: "my-deploy"},
+			},
+		},
+		"multipleOutputs": {
+			cue: `
+outputs: {
+  first: {
+    apiVersion: "v1"
+    kind: "ConfigMap"
+    metadata: { name: "cfg" }
+  }
+  second: {
+    apiVersion: "batch/v1"
+    kind: "Job"
+  }
+  third: { kind: "Service" } // missing apiVersion ignored
+}`,
+			want: []ResourceInfo{
+				{APIVersion: "v1", Kind: "ConfigMap", Name: "cfg"},
+				{APIVersion: "batch/v1", Kind: "Job", Name: "second"},
+			},
+		},
+		"parseError": {
+			cue: `
+output: {
+  apiVersion: "v1"
+  kind: "Pod"
+`,
+			wantErr: true,
+		},
+		"outputWithoutKindIgnored": {
+			cue: `
+output: {
+  apiVersion: "v1"
+  metadata: { name: "x" }
+}`,
+			want: []ResourceInfo{},
+		},
+		"metadataNameOverridesLabel": {
+			cue: `
+outputs: {
+  cfg: { apiVersion: "v1", kind: "ConfigMap", metadata: { name: "real" } }
+}`,
+			want: []ResourceInfo{
+				{APIVersion: "v1", Kind: "ConfigMap", Name: "real"},
+			},
+		},
+		"fallbackNameCoreGroup": {
+			cue: `
+outputs: {
+  svc: { apiVersion: "v1", kind: "Service" }
+}`,
+			want: []ResourceInfo{
+				{APIVersion: "v1", Kind: "Service", Name: "svc"},
+			},
+		},
+		"deepMetadataNameIgnoredFallbackToLabel": {
+			cue: `
+outputs: {
+  deep: { apiVersion: "v1", kind: "ConfigMap", metadata: { other: { name: "inner" } } }
+}`,
+			want: []ResourceInfo{
+				{APIVersion: "v1", Kind: "ConfigMap", Name: "deep"},
+			},
+		},
+		"nonStructOutputsValueIgnored": {
+			cue: `
+outputs: [
+  { apiVersion: "v1", kind: "ConfigMap" }
+]`,
+			want: []ResourceInfo{},
+		},
+		"missingAPIVersionAndKindEntriesIgnored": {
+			cue: `
+outputs: {
+  a: { apiVersion: "v1" }
+  b: { kind: "ConfigMap" }
+  c: { metadata: { name: "x" } }
+}`,
+			want: []ResourceInfo{},
+		},
+	}
+
+	for name, tc := range tests {
+		tc := tc
+		t.Run(name, func(t *testing.T) {
+			got, err := ExtractResourceInfo(tc.cue)
+			if tc.wantErr {
+				assert.Error(t, err)
+				return
+			}
+			assert.NoError(t, err)
+			if len(tc.want) == 0 {
+				assert.Len(t, got, 0)
+			} else {
+				assert.Equal(t, tc.want, got)
+			}
+		})
+	}
+}
+
+func TestValidateOutputResourcesExist(t *testing.T) {
+	// Enable the ValidateResourcesExist feature gate for these tests
+	originalState := utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateResourcesExist)
+	defer utilfeature.DefaultMutableFeatureGate.SetFromMap(map[string]bool{
+		string(features.ValidateResourcesExist): originalState,
+	})
+	utilfeature.DefaultMutableFeatureGate.SetFromMap(map[string]bool{
+		string(features.ValidateResourcesExist): true,
+	})
+
+	tests := map[string]struct {
+		cue     string
+		mapper  *fakeRESTMapper
+		wantErr string
+	}{
+		"singleSuccess": {
+			cue: `
+output: {
+  apiVersion: "apps/v1"
+  kind: "Deployment"
+  metadata: { name: "my-deploy" }
+}`,
+			mapper: newFakeRESTMapper([3]string{"apps", "Deployment", "v1"}),
+		},
+		"multipleSuccess": {
+			cue: `
+outputs: {
+  cm: { apiVersion: "v1", kind: "ConfigMap" }
+  jobRes: { apiVersion: "batch/v1", kind: "Job" }
+}`,
+			mapper: newFakeRESTMapper(
+				[3]string{"", "ConfigMap", "v1"},
+				[3]string{"batch", "Job", "v1"},
+			),
+		},
+		"missingResource": {
+			cue: `
+output: {
+  apiVersion: "apps/v1"
+  kind: "Deployment"
+}`,
+			mapper:  newFakeRESTMapper(),
+			wantErr: "resource type not found on cluster: apps/v1/Deployment",
+		},
+		"firstOkSecondMissing": {
+			cue: `
+outputs: {
+  a: { apiVersion: "v1", kind: "ConfigMap" }
+  b: { apiVersion: "batch/v1", kind: "Job" }
+}`,
+			mapper:  newFakeRESTMapper([3]string{"", "ConfigMap", "v1"}),
+			wantErr: "resource type not found on cluster: batch/v1/Job",
+		},
+		"ignoreIncompleteEntries": {
+			cue: `
+outputs: {
+  a: { apiVersion: "v1" }
+  b: { kind: "ConfigMap" }
+  c: { apiVersion: "v1", kind: "ConfigMap" }
+}`,
+			mapper: newFakeRESTMapper([3]string{"", "ConfigMap", "v1"}),
+		},
+		"duplicateResourceTypes": {
+			cue: `
+outputs: {
+  one: { apiVersion: "v1", kind: "ConfigMap" }
+  two: { apiVersion: "v1", kind: "ConfigMap" }
+}`,
+			mapper: newFakeRESTMapper([3]string{"", "ConfigMap", "v1"}),
+		},
+		"malformedApiVersionTrailingSlash": {
+			cue: `
+outputs: {
+  bad: { apiVersion: "apps/", kind: "Deployment" }
+}`,
+			mapper:  newFakeRESTMapper([3]string{"apps", "Deployment", "v1"}),
+			wantErr: "resource type not found on cluster: apps//Deployment",
+		},
+		"deepMetadataNameDoesNotAffectValidation": {
+			cue: `
+outputs: {
+  deep: { apiVersion: "v1", kind: "ConfigMap", metadata: { other: { name: "x" } } }
+}`,
+			mapper: newFakeRESTMapper([3]string{"", "ConfigMap", "v1"}),
+		},
+	}
+
+	for name, tc := range tests {
+		tc := tc
+		t.Run(name, func(t *testing.T) {
+			// Pass nil object for tests - feature gate is disabled by default
+			err := ValidateOutputResourcesExist(tc.cue, tc.mapper, nil)
+			if tc.wantErr != "" {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.wantErr)
+			} else {
+				assert.NoError(t, err)
+			}
+		})
+	}
+}
diff --git a/pkg/workflow/providers/legacy/query/handler_test.go b/pkg/workflow/providers/legacy/query/handler_test.go
index 94ffbc7a..35cc52b6 100644
--- a/pkg/workflow/providers/legacy/query/handler_test.go
+++ b/pkg/workflow/providers/legacy/query/handler_test.go
@@ -788,7 +788,7 @@ var _ = Describe("Test Query Provider", func() {
 			},
 			&networkv1.Ingress{
 				TypeMeta: metav1.TypeMeta{
-					APIVersion: "networking.k8s.io/v1beta1",
+					APIVersion: "networking.k8s.io/v1",
 				},
 				ObjectMeta: metav1.ObjectMeta{
 					Name:      "ingress-helm",
diff --git a/pkg/workflow/providers/query/handler_test.go b/pkg/workflow/providers/query/handler_test.go
index ecc43aa0..bdcc05f8 100644
--- a/pkg/workflow/providers/query/handler_test.go
+++ b/pkg/workflow/providers/query/handler_test.go
@@ -788,7 +788,7 @@ var _ = Describe("Test Query Provider", func() {
 			},
 			&networkv1.Ingress{
 				TypeMeta: metav1.TypeMeta{
-					APIVersion: "networking.k8s.io/v1beta1",
+					APIVersion: "networking.k8s.io/v1",
 				},
 				ObjectMeta: metav1.ObjectMeta{
 					Name:      "ingress-helm",
diff --git a/references/appfile/addon_test.go b/references/appfile/addon_test.go
index 93939c8f..a3790461 100644
--- a/references/appfile/addon_test.go
+++ b/references/appfile/addon_test.go
@@ -55,18 +55,18 @@ var _ = Describe("Test generateSecretFromTerraformOutput", func() {
 	var name = "test-addon-secret"
 	It("namespace doesn't exist", func() {
 		badNamespace := "a-not-existed-namespace"
-		err := generateSecretFromTerraformOutput(k8sClient, nil, name, badNamespace)
+		err := generateSecretFromTerraformOutput(k8sClient, "", name, badNamespace)
 		Expect(err).Should(Equal(fmt.Errorf("namespace %s doesn't exist", badNamespace)))
 	})
 	It("valid output list", func() {
-		outputList := []string{"name=aaa", "age=1"}
-		err := generateSecretFromTerraformOutput(k8sClient, outputList, name, addonNamespace)
+		rawOutput := "name=aaa\nage=1"
+		err := generateSecretFromTerraformOutput(k8sClient, rawOutput, name, addonNamespace)
 		Expect(err).Should(BeNil())
 	})
 
 	It("invalid output list", func() {
-		outputList := []string{"name"}
-		err := generateSecretFromTerraformOutput(k8sClient, outputList, name, addonNamespace)
-		Expect(err).Should(Equal(fmt.Errorf("terraform output isn't in the right format")))
+		rawOutput := "name"
+		err := generateSecretFromTerraformOutput(k8sClient, rawOutput, name, addonNamespace)
+		Expect(err).Should(Equal(fmt.Errorf("terraform output isn't in the right format: %q", rawOutput)))
 	})
 })
diff --git a/references/appfile/api/appfile_test.go b/references/appfile/api/appfile_test.go
index d1e647a7..074b874e 100644
--- a/references/appfile/api/appfile_test.go
+++ b/references/appfile/api/appfile_test.go
@@ -223,7 +223,7 @@ outputs: service: {
 }
 
 outputs: ingress: {
-  apiVersion: "networking.k8s.io/v1beta1"
+  apiVersion: "networking.k8s.io/v1"
   kind: "Ingress"
   spec: {
     rules: [{
@@ -232,9 +232,14 @@ outputs: ingress: {
         paths: [
           for k, v in parameter.http {
             path: k
+            pathType: "Prefix"
             backend: {
-              serviceName: context.name
-              servicePort: v
+              service: {
+                name: context.name
+                port: {
+                  number: v
+                }
+              }
             }
           }
         ]
diff --git a/references/cli/dryrun_test.go b/references/cli/dryrun_test.go
index cb29017e..90fdcbfb 100644
--- a/references/cli/dryrun_test.go
+++ b/references/cli/dryrun_test.go
@@ -285,4 +285,21 @@ var _ = Describe("Testing dry-run", func() {
 		Expect(buff.String()).Should(ContainSubstring(fmt.Sprintf("namespace: %s", appNamespace)))
 	})
 
+	It("Testing dry-run overriding conflicting namespace", func() {
+		// Prepare defs required (reuse existing component/trait definitions if present in cluster)
+		c := common2.Args{}
+		c.SetConfig(cfg)
+		c.SetClient(k8sClient)
+		// Ensure both namespaces exist
+		originalNS := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "original-ns"}}
+		_ = k8sClient.Create(context.Background(), originalNS)
+		forcedNS := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "forced-ns"}}
+		_ = k8sClient.Create(context.Background(), forcedNS)
+
+		opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-override.yaml"}, OfflineMode: false}
+		buff, err := DryRunApplication(&opt, c, "forced-ns", "")
+		Expect(err).Should(BeNil())
+		Expect(buff.String()).Should(ContainSubstring("namespace: forced-ns"))
+		Expect(buff.String()).ShouldNot(ContainSubstring("namespace: original-ns"))
+	})
 })
diff --git a/references/cli/top/model/suit_test.go b/references/cli/top/model/suit_test.go
index fcc1cec0..66a7aee5 100644
--- a/references/cli/top/model/suit_test.go
+++ b/references/cli/top/model/suit_test.go
@@ -120,7 +120,7 @@ var _ = BeforeSuite(func() {
 					Kind:       "Ingress",
 					Namespace:  "default",
 					Name:       "ingress-http",
-					APIVersion: "networking.k8s.io/v1beta1",
+					APIVersion: "networking.k8s.io/v1",
 				},
 			},
 			{
diff --git a/references/cli/velaql_test.go b/references/cli/velaql_test.go
index 7eb1c46c..f605813f 100644
--- a/references/cli/velaql_test.go
+++ b/references/cli/velaql_test.go
@@ -117,7 +117,7 @@ var _ = Describe("Test velaQL", func() {
 						Kind:       "Ingress",
 						Namespace:  "default",
 						Name:       "ingress-http",
-						APIVersion: "networking.k8s.io/v1beta1",
+						APIVersion: "networking.k8s.io/v1",
 					},
 				},
 				{
@@ -386,7 +386,7 @@ var _ = Describe("Test velaQL", func() {
 			},
 			&networkv1.Ingress{
 				TypeMeta: metav1.TypeMeta{
-					APIVersion: "networking.k8s.io/v1beta1",
+					APIVersion: "networking.k8s.io/v1",
 				},
 				ObjectMeta: metav1.ObjectMeta{
 					Name:      "ingress-helm",
diff --git a/references/common/application_test.go b/references/common/application_test.go
index e8da691d..86c88a3f 100644
--- a/references/common/application_test.go
+++ b/references/common/application_test.go
@@ -16,22 +16,111 @@ limitations under the License.
 package common
 
 import (
+	"bytes"
 	"context"
+	"os"
 	"testing"
 
 	terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
-	"sigs.k8s.io/controller-runtime/pkg/client"
+	sigs "sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	"github.com/oam-dev/kubevela/apis/types"
+	"github.com/oam-dev/kubevela/references/appfile/api"
+
+	utilcommon "github.com/oam-dev/kubevela/pkg/utils/common"
+	querytypes "github.com/oam-dev/kubevela/pkg/utils/types"
+	cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
 )
 
+func TestExportFromAppFile(t *testing.T) {
+	s := runtime.NewScheme()
+	assert.NoError(t, v1beta1.AddToScheme(s))
+	clt := fake.NewClientBuilder().WithScheme(s).Build()
+	var out bytes.Buffer
+	ioStream := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+	o := &AppfileOptions{
+		Kubecli:   clt,
+		IO:        ioStream,
+		Namespace: "default",
+	}
+
+	appFile := &api.AppFile{
+		Name: "test-app-export-from",
+	}
+
+	c := utilcommon.Args{}
+	c.SetClient(clt)
+
+	result, data, err := o.ExportFromAppFile(appFile, "default", true, c)
+	assert.NoError(t, err)
+	assert.NotNil(t, result)
+	assert.NotNil(t, data)
+	assert.Contains(t, string(data), "name: test-app-export-from")
+	assert.Equal(t, "test-app-export-from", result.application.Name)
+}
+
+func TestApplyApp(t *testing.T) {
+	s := runtime.NewScheme()
+	v1beta1.AddToScheme(s)
+
+	t.Run("create app if not exist", func(t *testing.T) {
+		cltCreate := fake.NewClientBuilder().WithScheme(s).Build()
+		var outCreate bytes.Buffer
+		ioStreamCreate := cmdutil.IOStreams{In: os.Stdin, Out: &outCreate, ErrOut: &outCreate}
+		oCreate := &AppfileOptions{
+			Kubecli:   cltCreate,
+			IO:        ioStreamCreate,
+			Namespace: "default",
+		}
+		appCreate := &v1beta1.Application{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-app-apply",
+				Namespace: "default",
+			},
+		}
+		err := oCreate.ApplyApp(appCreate, nil)
+		assert.NoError(t, err)
+		assert.Contains(t, outCreate.String(), "App has not been deployed, creating a new deployment...")
+		assert.Contains(t, outCreate.String(), "vela port-forward test-app-apply")
+	})
+
+	t.Run("update app if exists", func(t *testing.T) {
+		existingApp := &v1beta1.Application{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-app-apply",
+				Namespace: "default",
+			},
+		}
+		cltUpdate := fake.NewClientBuilder().WithScheme(s).WithObjects(existingApp).Build()
+		var outUpdate bytes.Buffer
+		ioStreamUpdate := cmdutil.IOStreams{In: os.Stdin, Out: &outUpdate, ErrOut: &outUpdate}
+		oUpdate := &AppfileOptions{
+			Kubecli:   cltUpdate,
+			IO:        ioStreamUpdate,
+			Namespace: "default",
+		}
+		appUpdate := &v1beta1.Application{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-app-apply",
+				Namespace: "default",
+			},
+		}
+		err := oUpdate.ApplyApp(appUpdate, nil)
+		assert.NoError(t, err)
+		assert.Contains(t, outUpdate.String(), "App exists, updating existing deployment...")
+		assert.Contains(t, outUpdate.String(), "vela port-forward test-app-apply")
+	})
+}
+
 func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
 	ctx := context.Background()
 	s := runtime.NewScheme()
@@ -95,7 +184,7 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
 
 	k8sClient5 := fake.NewClientBuilder().WithScheme(s).WithObjects(app2, def2, conf2).Build()
 	type args struct {
-		k8sClient client.Client
+		k8sClient sigs.Client
 		namespace string
 		name      string
 	}
@@ -176,5 +265,115 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
 			}
 		})
 	}
+}
+
+func TestIsAppfile(t *testing.T) {
+	testCases := []struct {
+		name     string
+		data     []byte
+		expected bool
+	}{
+		{
+			name:     "valid appfile json",
+			data:     []byte(`{"name": "test"}`),
+			expected: true,
+		},
+		{
+			name:     "invalid json",
+			data:     []byte(`{"name": "test"`),
+			expected: false,
+		},
+		{
+			name: "application yaml",
+			data: []byte(`
+apiVersion: core.oam.dev/v1beta1
+kind: Application
+metadata:
+  name: test-app
+`),
+			expected: false,
+		},
+		{
+			name: "appfile yaml",
+			data: []byte(`
+name: test-app
+`),
+			expected: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := IsAppfile(tc.data)
+			assert.Equal(t, tc.expected, result)
+		})
+	}
+}
+
+func TestInfo(t *testing.T) {
+	app := &v1beta1.Application{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-app",
+			Namespace: "test-ns",
+		},
+	}
+	info := Info(app)
+	assert.Contains(t, info, "vela port-forward test-app -n test-ns")
+	assert.Contains(t, info, "vela exec test-app -n test-ns")
+	assert.Contains(t, info, "vela logs test-app -n test-ns")
+	assert.Contains(t, info, "vela status test-app -n test-ns")
+	assert.Contains(t, info, "vela status test-app -n test-ns --endpoint")
+}
 
+func TestSonLeafResource(t *testing.T) {
+	node := &querytypes.ResourceTreeNode{
+		LeafNodes: []*querytypes.ResourceTreeNode{
+			{
+				Object: &unstructured.Unstructured{
+					Object: map[string]interface{}{
+						"kind":       "Deployment",
+						"apiVersion": "apps/v1",
+					},
+				},
+				Kind:       "Deployment",
+				APIVersion: "apps/v1",
+			},
+		},
+	}
+	resources := sonLeafResource(node, "Deployment", "apps/v1")
+	assert.Equal(t, 1, len(resources))
+	assert.Equal(t, "Deployment", resources[0].GetKind())
+}
+
+func TestLoadAppFile(t *testing.T) {
+	content := "name: test-app"
+	tmpFile, err := os.CreateTemp("", "appfile-*.yaml")
+	assert.NoError(t, err)
+	defer os.Remove(tmpFile.Name())
+
+	_, err = tmpFile.WriteString(content)
+	assert.NoError(t, err)
+	assert.NoError(t, tmpFile.Close())
+
+	appFile, err := LoadAppFile(tmpFile.Name())
+	assert.NoError(t, err)
+	assert.NotNil(t, appFile)
+	assert.Equal(t, "test-app", appFile.Name)
+}
+
+func TestApplyApplication(t *testing.T) {
+	s := runtime.NewScheme()
+	v1beta1.AddToScheme(s)
+	clt := fake.NewClientBuilder().WithScheme(s).Build()
+	app := v1beta1.Application{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-app",
+			Namespace: "default",
+		},
+	}
+	var out bytes.Buffer
+	ioStream := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+	err := ApplyApplication(app, ioStream, clt)
+	assert.NoError(t, err)
+	assert.Contains(t, out.String(), "Applying an application in vela K8s object format...")
 }
diff --git a/references/common/metrics_test.go b/references/common/metrics_test.go
index e751aa90..5ab7044c 100644
--- a/references/common/metrics_test.go
+++ b/references/common/metrics_test.go
@@ -18,39 +18,50 @@ package common
 
 import (
 	"testing"
-	"time"
-
-	"k8s.io/utils/ptr"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-
-	"github.com/oam-dev/kubevela/pkg/utils/common"
 
 	"github.com/stretchr/testify/assert"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/metrics/pkg/apis/metrics/v1beta1"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	appv1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 )
 
 func TestToPercentageStr(t *testing.T) {
-	var v1, v2 int64
-	v1, v2 = 10, 100
-	assert.Equal(t, ToPercentageStr(v1, v2), "10%")
-	v1, v2 = 10, 0
-	assert.Equal(t, ToPercentageStr(v1, v2), "N/A")
+	t.Run("valid percentage", func(t *testing.T) {
+		var v1, v2 int64 = 10, 100
+		assert.Equal(t, "10%", ToPercentageStr(v1, v2))
+	})
+	t.Run("division by zero", func(t *testing.T) {
+		var v1, v2 int64 = 10, 0
+		assert.Equal(t, "N/A", ToPercentageStr(v1, v2))
+	})
 }
 
-func TestGetPodResourceSpecAndUsage(t *testing.T) {
-	testEnv := &envtest.Environment{
-		ControlPlaneStartTimeout: time.Minute * 3,
-		ControlPlaneStopTimeout:  time.Minute,
-		UseExistingCluster:       ptr.To(false),
-	}
-	cfg, err := testEnv.Start()
-	assert.NoError(t, err)
+func TestToPercentage(t *testing.T) {
+	t.Run("valid percentage", func(t *testing.T) {
+		var v1, v2 int64 = 10, 100
+		assert.Equal(t, 10, ToPercentage(v1, v2))
+	})
+	t.Run("division by zero", func(t *testing.T) {
+		var v1, v2 int64 = 10, 0
+		assert.Equal(t, 0, ToPercentage(v1, v2))
+	})
+	t.Run("floor", func(t *testing.T) {
+		var v1, v2 int64 = 1, 3
+		assert.Equal(t, 33, ToPercentage(v1, v2))
+	})
+}
 
-	k8sClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
-	assert.NoError(t, err)
+func TestGetPodResourceSpecAndUsage(t *testing.T) {
+	s := runtime.NewScheme()
+	v1.AddToScheme(s)
+	k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
 
 	quantityLimitsCPU, _ := resource.ParseQuantity("10m")
 	quantityLimitsMemory, _ := resource.ParseQuantity("10Mi")
@@ -83,10 +94,157 @@ func TestGetPodResourceSpecAndUsage(t *testing.T) {
 	}
 
 	spec, usage := GetPodResourceSpecAndUsage(k8sClient, pod, podMetric)
-	assert.Equal(t, usage.CPU, int64(8))
-	assert.Equal(t, usage.Mem, int64(20971520))
-	assert.Equal(t, spec.Lcpu, int64(10))
-	assert.Equal(t, spec.Lmem, int64(10485760))
-	assert.Equal(t, spec.Rcpu, int64(100))
-	assert.Equal(t, spec.Rmem, int64(52428800))
+	assert.Equal(t, int64(8), usage.CPU)
+	assert.Equal(t, int64(20971520), usage.Mem)
+	assert.Equal(t, int64(10), spec.Lcpu)
+	assert.Equal(t, int64(10485760), spec.Lmem)
+	assert.Equal(t, int64(100), spec.Rcpu)
+	assert.Equal(t, int64(52428800), spec.Rmem)
+}
+
+func TestGetPodStorage(t *testing.T) {
+	s := runtime.NewScheme()
+	v1.AddToScheme(s)
+
+	pvc := &v1.PersistentVolumeClaim{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-pvc",
+			Namespace: "default",
+		},
+	}
+	k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(pvc).Build()
+
+	podWithPVC := &v1.Pod{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "default",
+		},
+		Spec: v1.PodSpec{
+			Volumes: []v1.Volume{
+				{
+					Name: "test-storage",
+					VolumeSource: v1.VolumeSource{
+						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
+							ClaimName: "test-pvc",
+						},
+					},
+				},
+			},
+		},
+	}
+
+	podWithoutPVC := &v1.Pod{
+		Spec: v1.PodSpec{
+			Volumes: []v1.Volume{
+				{
+					Name: "test-emptydir",
+					VolumeSource: v1.VolumeSource{
+						EmptyDir: &v1.EmptyDirVolumeSource{},
+					},
+				},
+			},
+		},
+	}
+
+	podWithNonExistentPVC := &v1.Pod{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "default",
+		},
+		Spec: v1.PodSpec{
+			Volumes: []v1.Volume{
+				{
+					Name: "test-storage",
+					VolumeSource: v1.VolumeSource{
+						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
+							ClaimName: "non-existent-pvc",
+						},
+					},
+				},
+			},
+		},
+	}
+
+	t.Run("pod with pvc", func(t *testing.T) {
+		storages := GetPodStorage(k8sClient, podWithPVC)
+		assert.Equal(t, 1, len(storages))
+		assert.Equal(t, "test-pvc", storages[0].Name)
+	})
+
+	t.Run("pod without pvc", func(t *testing.T) {
+		storages := GetPodStorage(k8sClient, podWithoutPVC)
+		assert.Equal(t, 0, len(storages))
+	})
+
+	t.Run("pod with non-existent pvc", func(t *testing.T) {
+		storages := GetPodStorage(k8sClient, podWithNonExistentPVC)
+		assert.Equal(t, 0, len(storages))
+	})
+}
+
+func TestGetPodOfManagedResource(t *testing.T) {
+	s := runtime.NewScheme()
+	v1.AddToScheme(s)
+	appv1beta1.AddToScheme(s)
+
+	app := &appv1beta1.Application{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-app",
+			Namespace: "default",
+		},
+	}
+
+	pod := &v1.Pod{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Pod",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-pod",
+			Namespace: "default",
+		},
+	}
+	podUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
+	assert.NoError(t, err)
+
+	rt := &appv1beta1.ResourceTracker{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-app-v1", // A tracker name
+			Namespace: "default",
+			Labels: map[string]string{
+				"app.oam.dev/name":      "test-app",
+				"app.oam.dev/namespace": "default",
+			},
+		},
+		Spec: appv1beta1.ResourceTrackerSpec{
+			Type: appv1beta1.ResourceTrackerTypeRoot,
+			ManagedResources: []appv1beta1.ManagedResource{
+				{
+					ClusterObjectReference: common.ClusterObjectReference{
+						ObjectReference: v1.ObjectReference{
+							Kind:       "Pod",
+							APIVersion: "v1",
+							Name:       "test-pod",
+							Namespace:  "default",
+						},
+					},
+					OAMObjectReference: common.OAMObjectReference{
+						Component: "test-component",
+					},
+				},
+			},
+		},
+	}
+
+	k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(app, rt, &unstructured.Unstructured{Object: podUnstructured}).Build()
+
+	t.Run("get existing pod", func(t *testing.T) {
+		pods := GetPodOfManagedResource(k8sClient, app, "test-component")
+		assert.Equal(t, 2, len(pods))
+		assert.Equal(t, "test-pod", pods[0].Name)
+		assert.Equal(t, "test-pod", pods[1].Name)
+	})
+
+	t.Run("get pod for non-existent component", func(t *testing.T) {
+		pods := GetPodOfManagedResource(k8sClient, app, "non-existent-component")
+		assert.Equal(t, 0, len(pods))
+	})
 }
diff --git a/references/common/registry_test.go b/references/common/registry_test.go
index 44c6fe79..d484ad08 100644
--- a/references/common/registry_test.go
+++ b/references/common/registry_test.go
@@ -17,16 +17,178 @@ limitations under the License.
 package common
 
 import (
+	"bytes"
+	"context"
 	"encoding/json"
+	"os"
 	"reflect"
 	"testing"
 
+	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+	"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
 
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	"github.com/oam-dev/kubevela/apis/types"
+	cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
 )
 
+func TestInstallComponentDefinition(t *testing.T) {
+	s := runtime.NewScheme()
+	assert.NoError(t, v1beta1.AddToScheme(s))
+
+	validComponentData := []byte(`
+apiVersion: core.oam.dev/v1beta1
+kind: ComponentDefinition
+metadata:
+  name: test-component
+spec:
+  workload:
+    definition:
+      apiVersion: apps/v1
+      kind: Deployment
+`)
+
+	existingComponent := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-component",
+			Namespace: types.DefaultKubeVelaNS,
+		},
+	}
+
+	t.Run("success", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
+		assert.NoError(t, err)
+		assert.Contains(t, out.String(), "Installing component: test-component")
+
+		var cd v1beta1.ComponentDefinition
+		err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "test-component", Namespace: types.DefaultKubeVelaNS}, &cd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test-component", cd.Name)
+	})
+
+	t.Run("already exists", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(existingComponent).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
+		assert.NoError(t, err)
+	})
+
+	t.Run("client error", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithInterceptorFuncs(interceptor.Funcs{
+			Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
+				return errors.New("client create error")
+			},
+		}).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
+		assert.Error(t, err)
+		assert.EqualError(t, err, "client create error")
+	})
+
+	t.Run("invalid data", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+		invalidData := []byte(`{"invalid": "yaml"`)
+
+		err := InstallComponentDefinition(k8sClient, invalidData, ioStreams)
+		assert.Error(t, err)
+	})
+
+	t.Run("nil data", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallComponentDefinition(k8sClient, nil, ioStreams)
+		assert.Error(t, err)
+		assert.Equal(t, "componentData is nil", err.Error())
+	})
+}
+
+func TestInstallTraitDefinition(t *testing.T) {
+	s := runtime.NewScheme()
+	v1beta1.AddToScheme(s)
+
+	validTraitData := []byte(`
+apiVersion: core.oam.dev/v1beta1
+kind: TraitDefinition
+metadata:
+  name: test-trait
+spec:
+  appliesToWorkloads:
+    - deployments.apps
+`)
+
+	existingTrait := &v1beta1.TraitDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-trait",
+			Namespace: types.DefaultKubeVelaNS,
+		},
+	}
+
+	t.Run("success", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
+		assert.NoError(t, err)
+		assert.Contains(t, out.String(), "Installing trait test-trait")
+
+		var td v1beta1.TraitDefinition
+		err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "test-trait", Namespace: types.DefaultKubeVelaNS}, &td)
+		assert.NoError(t, err)
+		assert.Equal(t, "test-trait", td.Name)
+	})
+
+	t.Run("already exists", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(existingTrait).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
+		assert.NoError(t, err)
+	})
+
+	t.Run("client error", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithInterceptorFuncs(interceptor.Funcs{
+			Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
+				return errors.New("client create error")
+			},
+		}).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+
+		err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
+		assert.Error(t, err)
+		assert.Equal(t, "client create error", err.Error())
+	})
+
+	t.Run("invalid data", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		var out bytes.Buffer
+		ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
+		invalidData := []byte(`{"invalid": "yaml"`)
+
+		err := InstallTraitDefinition(k8sClient, invalidData, ioStreams)
+		assert.Error(t, err)
+	})
+}
+
 func TestAddSourceIntoDefinition(t *testing.T) {
 	caseJson := []byte(`{"template":""}`)
 	wantJson := []byte(`{"source":{"repoName":"foo"},"template":""}`)
diff --git a/references/common/trait_test.go b/references/common/trait_test.go
new file mode 100644
index 00000000..20879c10
--- /dev/null
+++ b/references/common/trait_test.go
@@ -0,0 +1,90 @@
+/*
+Copyright 2021 The KubeVela 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 (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	"github.com/oam-dev/kubevela/pkg/utils/common"
+)
+
+func TestListRawWorkloadDefinitions(t *testing.T) {
+	s := runtime.NewScheme()
+	v1beta1.AddToScheme(s)
+
+	sysWorkload := v1beta1.WorkloadDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "sys-worker",
+			Namespace: oam.SystemDefinitionNamespace,
+		},
+	}
+	userWorkload := v1beta1.WorkloadDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "user-worker",
+			Namespace: "my-ns",
+		},
+	}
+
+	t.Run("list from both user and system namespaces", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&sysWorkload, &userWorkload).Build()
+		c := common.Args{}
+		c.SetClient(k8sClient)
+
+		defs, err := ListRawWorkloadDefinitions("my-ns", c)
+		assert.NoError(t, err)
+		assert.Equal(t, 2, len(defs))
+	})
+
+	t.Run("list from only system namespace", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&sysWorkload).Build()
+		c := common.Args{}
+		c.SetClient(k8sClient)
+
+		defs, err := ListRawWorkloadDefinitions("my-ns", c)
+		assert.NoError(t, err)
+		assert.Equal(t, 1, len(defs))
+		assert.Equal(t, "sys-worker", defs[0].Name)
+	})
+
+	t.Run("list from only user namespace", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&userWorkload).Build()
+		c := common.Args{}
+		c.SetClient(k8sClient)
+
+		defs, err := ListRawWorkloadDefinitions("my-ns", c)
+		assert.NoError(t, err)
+		assert.Equal(t, 1, len(defs))
+		assert.Equal(t, "user-worker", defs[0].Name)
+	})
+
+	t.Run("no definitions found", func(t *testing.T) {
+		k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
+		c := common.Args{}
+		c.SetClient(k8sClient)
+
+		defs, err := ListRawWorkloadDefinitions("my-ns", c)
+		assert.NoError(t, err)
+		assert.Equal(t, 0, len(defs))
+	})
+}
diff --git a/references/common/workload_test.go b/references/common/workload_test.go
new file mode 100644
index 00000000..4549d95a
--- /dev/null
+++ b/references/common/workload_test.go
@@ -0,0 +1,106 @@
+/*
+Copyright 2021 The KubeVela 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 (
+	"testing"
+
+	"github.com/spf13/pflag"
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	corecommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/apis/types"
+	"github.com/oam-dev/kubevela/pkg/oam"
+	"github.com/oam-dev/kubevela/pkg/utils/common"
+)
+
+func TestInitApplication(t *testing.T) {
+	c := common.Args{}
+	c.SetClient(fake.NewClientBuilder().Build())
+
+	t.Run("with appGroup", func(t *testing.T) {
+		app, err := InitApplication("default", c, "my-workload", "my-app")
+		assert.NoError(t, err)
+		assert.NotNil(t, app)
+		assert.Equal(t, "my-app", app.Name)
+	})
+
+	t.Run("without appGroup", func(t *testing.T) {
+		app, err := InitApplication("default", c, "my-workload", "")
+		assert.NoError(t, err)
+		assert.NotNil(t, app)
+		assert.Equal(t, "my-workload", app.Name)
+	})
+}
+
+func TestBaseComplete(t *testing.T) {
+	s := runtime.NewScheme()
+	assert.NoError(t, v1beta1.AddToScheme(s))
+
+	template := &v1beta1.ComponentDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "worker",
+			Namespace: oam.SystemDefinitionNamespace,
+		},
+		Spec: v1beta1.ComponentDefinitionSpec{
+			Workload: corecommon.WorkloadTypeDescriptor{
+				Type: types.AutoDetectWorkloadDefinition,
+			},
+			Schematic: &corecommon.Schematic{
+				CUE: &corecommon.CUE{
+					Template: `
+parameter: {
+	image: string
+	port: *8080 | int
+}
+`,
+				},
+			},
+		},
+	}
+	k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(template).Build()
+	c := common.Args{}
+	c.SetClient(k8sClient)
+
+	t.Run("success", func(t *testing.T) {
+		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
+		flagSet.String("image", "my-image", "")
+		flagSet.Int64("port", 80, "")
+
+		app, err := BaseComplete(oam.SystemDefinitionNamespace, c, "my-workload", "my-app", flagSet, "worker")
+		assert.NoError(t, err)
+		assert.NotNil(t, app)
+
+		workload, ok := app.Services["my-workload"]
+		assert.True(t, ok)
+		assert.Equal(t, "my-image", workload["image"])
+		assert.Equal(t, int64(80), workload["port"])
+	})
+
+	t.Run("missing required flag", func(t *testing.T) {
+		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
+		// not setting the required "image" flag
+
+		_, err := BaseComplete(oam.SystemDefinitionNamespace, c, "my-workload", "my-app", flagSet, "worker")
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), `required flag(s) "image" not set`)
+	})
+}
diff --git a/test/e2e-addon-test/addon_test.go b/test/e2e-addon-test/addon_test.go
index 6385bff0..fcccfa9c 100644
--- a/test/e2e-addon-test/addon_test.go
+++ b/test/e2e-addon-test/addon_test.go
@@ -21,17 +21,22 @@ import (
 	"errors"
 	"fmt"
 	"math/rand"
+
 	"os/exec"
 	"strconv"
+
 	"time"
 
 	terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
+
 	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
 	"github.com/oam-dev/kubevela/pkg/oam/util"
 	"github.com/oam-dev/kubevela/pkg/utils/common"
@@ -132,4 +137,41 @@ var _ = Describe("Addon tests", func() {
 		Expect(err).Should(BeNil())
 		Expect(string(output)).Should(ContainSubstring("enabled successfully"))
 	})
+
+	It("Addon Workflow is successfully enabled and WorkflowRun creates Deployment", func() {
+		By("Install Addon Workflow")
+
+		output, err := exec.Command("bash", "-c", "/tmp/vela addon enable vela-workflow").Output()
+		var ee *exec.ExitError
+		if errors.As(err, &ee) {
+			fmt.Println("exit code error:", string(ee.Stderr))
+		}
+		Expect(err).Should(BeNil())
+		Expect(string(output)).Should(ContainSubstring("enabled successfully"))
+
+		By("Apply a WorkflowRun which creates a Deployment")
+		var wr workflowv1alpha1.WorkflowRun
+		Expect(common.ReadYamlToObject("./testdata/workflow/workflowrun_nginx.yaml", &wr)).Should(BeNil())
+		// set namespace to dynamically created test namespace
+		wr.Namespace = namespaceName
+		Eventually(func() error { return k8sClient.Create(ctx, wr.DeepCopy()) }, 20*time.Second, 500*time.Millisecond).Should(Succeed())
+
+		By("Verify the Deployment is created by WorkflowRun")
+		Eventually(func() error {
+			var deploy appsv1.Deployment
+			return k8sClient.Get(ctx, client.ObjectKey{Name: "apply-nginx-deployment", Namespace: namespaceName}, &deploy)
+		}, 180*time.Second, 2*time.Second).Should(Succeed())
+
+		By("Check the WorkflowRun reaches a succeeded phase")
+		Eventually(func() error {
+			var latest workflowv1alpha1.WorkflowRun
+			if err := k8sClient.Get(ctx, client.ObjectKey{Name: wr.Name, Namespace: namespaceName}, &latest); err != nil {
+				return err
+			}
+			if latest.Status.Phase == workflowv1alpha1.WorkflowStateSucceeded {
+				return nil
+			}
+			return fmt.Errorf("workflowrun not finished, current phase: %s", latest.Status.Phase)
+		}, 300*time.Second, 2*time.Second).Should(Succeed())
+	})
 })
diff --git a/test/e2e-addon-test/suite_test.go b/test/e2e-addon-test/suite_test.go
index c68fcee0..a8a00230 100644
--- a/test/e2e-addon-test/suite_test.go
+++ b/test/e2e-addon-test/suite_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 	"time"
 
+	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
 	terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -57,6 +58,8 @@ var _ = BeforeSuite(func() {
 	Expect(err).Should(BeNil())
 	err = terraformv1beta1.AddToScheme(scheme)
 	Expect(err).Should(BeNil())
+	err = workflowv1alpha1.AddToScheme(scheme)
+	Expect(err).Should(BeNil())
 	By("Setting up kubernetes client")
 	k8sClient, err = client.New(config.GetConfigOrDie(), client.Options{Scheme: scheme})
 	if err != nil {
diff --git a/test/e2e-addon-test/testdata/app/app_terraform_pod.yaml b/test/e2e-addon-test/testdata/app/app_terraform_pod.yaml
new file mode 100644
index 00000000..94f818ce
--- /dev/null
+++ b/test/e2e-addon-test/testdata/app/app_terraform_pod.yaml
@@ -0,0 +1,12 @@
+apiVersion: core.oam.dev/v1beta1
+kind: Application
+metadata:
+  name: terraform-pod-sample
+spec:
+  components:
+    - name: sample-pod
+      type: terraform-k8s-pod
+      properties:
+        name: tf-sample-pod
+        namespace: ___NAMESPACE___
+        image: nginx:1.25-alpine
diff --git a/test/e2e-addon-test/testdata/definitions/terraform_pod_component.yaml b/test/e2e-addon-test/testdata/definitions/terraform_pod_component.yaml
new file mode 100644
index 00000000..a5c0859f
--- /dev/null
+++ b/test/e2e-addon-test/testdata/definitions/terraform_pod_component.yaml
@@ -0,0 +1,65 @@
+apiVersion: core.oam.dev/v1beta1
+kind: ComponentDefinition
+metadata:
+  name: terraform-k8s-pod
+  namespace: vela-system
+  annotations:
+    definition.oam.dev/description: Terraform configuration to create a Kubernetes Pod via the kubernetes provider
+  labels:
+    type: terraform
+spec:
+  workload:
+    definition:
+      apiVersion: terraform.core.oam.dev/v1beta1
+      kind: Configuration
+  schematic:
+    terraform:
+      configuration: |
+        terraform {
+          required_providers {
+            kubernetes = {
+              source  = "hashicorp/kubernetes"
+              version = "~> 2.27"
+            }
+          }
+        }
+
+        # In-cluster provider auth (terraform-controller should run in cluster and inject)
+        provider "kubernetes" {}
+
+        resource "kubernetes_pod" "example" {
+          metadata {
+            name      = var.name
+            namespace = var.namespace
+            labels = {
+              app = "tf-sample"
+            }
+          }
+          spec {
+            container {
+              image = var.image
+              name  = "nginx"
+              port {
+                container_port = 80
+              }
+            }
+          }
+        }
+
+        output "POD_NAME" {
+          value = kubernetes_pod.example.metadata[0].name
+        }
+
+        variable "name" {
+          type = string
+          description = "Name of the Pod to create"
+        }
+        variable "namespace" {
+          type = string
+          description = "Namespace in which to create the Pod"
+        }
+        variable "image" {
+          type    = string
+          default = "nginx:1.25-alpine"
+          description = "Container image"
+        }
diff --git a/test/e2e-addon-test/testdata/workflow/workflowrun_nginx.yaml b/test/e2e-addon-test/testdata/workflow/workflowrun_nginx.yaml
new file mode 100644
index 00000000..6a1f06d5
--- /dev/null
+++ b/test/e2e-addon-test/testdata/workflow/workflowrun_nginx.yaml
@@ -0,0 +1,13 @@
+apiVersion: core.oam.dev/v1alpha1
+kind: WorkflowRun
+metadata:
+  name: deploy-nginx
+  namespace: default
+spec:
+  workflowSpec:
+    steps:
+      - name: apply-nginx-deployment
+        type: apply-deployment
+        properties:
+          image: nginx:latest
+          replicas: 1 
\ No newline at end of file
diff --git a/test/e2e-test/definition_output_validation_test.go b/test/e2e-test/definition_output_validation_test.go
new file mode 100644
index 00000000..567dbff2
--- /dev/null
+++ b/test/e2e-test/definition_output_validation_test.go
@@ -0,0 +1,1188 @@
+/*
+ Copyright 2021. The KubeVela 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 controllers_test
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
+	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
+	"github.com/oam-dev/kubevela/pkg/oam/util"
+)
+
+var _ = Describe("Definition Output Validation E2E tests", func() {
+	ctx := context.Background()
+
+	var namespace string
+	var ns corev1.Namespace
+
+	BeforeEach(func() {
+		namespace = randomNamespaceName("def-output-validation")
+		ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
+
+		Eventually(func() error {
+			return k8sClient.Create(ctx, &ns)
+		}, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
+	})
+
+	AfterEach(func() {
+		By("Clean up resources after a test")
+		k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
+		k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
+		k8sClient.DeleteAllOf(ctx, &v1beta1.PolicyDefinition{}, client.InNamespace(namespace))
+		k8sClient.DeleteAllOf(ctx, &v1beta1.WorkflowStepDefinition{}, client.InNamespace(namespace))
+
+		By(fmt.Sprintf("Delete the entire namespace %s", ns.Name))
+		Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
+	})
+
+	Context("Test validation for outputs with non-existent CRDs", func() {
+
+		It("Should reject ComponentDefinition with non-existent CRD in outputs", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-with-invalid-output",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	nonExistentResource: {
+		apiVersion: "custom.io/v1alpha1"
+		kind: "NonExistentResource"
+		metadata: name: parameter.name + "-custom"
+		spec: {
+			foo: "bar"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept ComponentDefinition with only valid resources in outputs", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-with-valid-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{
+				port: 80
+				targetPort: 8080
+			}]
+		}
+	}
+	configmap: {
+		apiVersion: "v1"
+		kind: "ConfigMap"
+		metadata: name: parameter.name + "-config"
+		data: {
+			config: "some-config"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, componentDef)).Should(Succeed())
+		})
+
+		It("Should reject TraitDefinition with non-existent CRD in outputs", func() {
+			traitDef := &v1beta1.TraitDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "TraitDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-trait-with-invalid-output",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.TraitDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	replicas: int
+}
+
+outputs: {
+	hpa: {
+		apiVersion: "autoscaling/v2"
+		kind: "HorizontalPodAutoscaler"
+		metadata: name: context.name + "-hpa"
+		spec: {
+			scaleTargetRef: {
+				apiVersion: "apps/v1"
+				kind: "Deployment"
+				name: context.name
+			}
+			minReplicas: parameter.replicas
+			maxReplicas: parameter.replicas * 2
+		}
+	}
+	customResource: {
+		apiVersion: "nonexistent.io/v1"
+		kind: "NonExistentCRD"
+		metadata: name: context.name + "-custom"
+		spec: {
+			data: "test"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, traitDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept TraitDefinition with valid resources", func() {
+			traitDef := &v1beta1.TraitDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "TraitDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-trait-with-valid-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.TraitDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	replicas: int
+}
+
+patch: {
+	spec: replicas: parameter.replicas
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, traitDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, traitDef)).Should(Succeed())
+		})
+
+		It("Should reject PolicyDefinition with non-existent CRD in outputs", func() {
+			policyDef := &v1beta1.PolicyDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "PolicyDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-policy-with-invalid-output",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.PolicyDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	namespace: string
+}
+
+output: {
+	apiVersion: "v1"
+	kind: "Namespace"
+	metadata: name: parameter.namespace
+}
+
+outputs: {
+	invalidResource: {
+		apiVersion: "custom.io/v1beta1"
+		kind: "CustomPolicy"
+		metadata: name: parameter.namespace + "-policy"
+		spec: {
+			enabled: true
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, policyDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept PolicyDefinition with valid resources", func() {
+			policyDef := &v1beta1.PolicyDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "PolicyDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-policy-with-valid-output",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.PolicyDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	namespace: string
+}
+
+output: {
+	apiVersion: "v1"
+	kind: "Namespace"
+	metadata: name: parameter.namespace
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, policyDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, policyDef)).Should(Succeed())
+		})
+
+		It("Should handle definitions with mixed valid and non-K8s resources", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-with-mixed-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{port: 80}]
+		}
+	}
+	customData: {
+		field1: "value1"
+		field2: "value2"
+		nested: {
+			data: "some-data"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, componentDef)).Should(Succeed())
+		})
+
+		It("Should handle update operations with validation", func() {
+			// First create a valid definition
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-update-validation",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: "app"
+				image: "nginx"
+			}]
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Try to update with invalid output
+			Eventually(func() error {
+				var existing v1beta1.ComponentDefinition
+				if err := k8sClient.Get(ctx, client.ObjectKey{Name: componentDef.Name, Namespace: namespace}, &existing); err != nil {
+					return err
+				}
+				existing.Spec.Schematic.CUE.Template = `
+parameter: {
+	name: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: "app"
+				image: "nginx"
+			}]
+		}
+	}
+}
+
+outputs: {
+	invalidCRD: {
+		apiVersion: "nonexistent.io/v1"
+		kind: "NonExistentResource"
+		metadata: name: parameter.name
+		spec: {}
+	}
+}`
+				return k8sClient.Update(ctx, &existing)
+			}, time.Second*5, time.Millisecond*500).Should(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, componentDef)).Should(Succeed())
+		})
+
+		It("Should reject ComponentDefinition with multiple invalid CRDs", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-multiple-invalid",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+outputs: {
+	validService: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{port: 80}]
+		}
+	}
+	invalidCRD1: {
+		apiVersion: "custom.io/v1alpha1"
+		kind: "CustomResource"
+		metadata: name: parameter.name + "-custom1"
+		spec: {foo: "bar"}
+	}
+	invalidCRD2: {
+		apiVersion: "another.io/v1beta1"
+		kind: "AnotherResource"
+		metadata: name: parameter.name + "-custom2"
+		spec: {enabled: true}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should handle definitions with complex CUE expressions", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-cue-expressions",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+import "strings"
+
+parameter: {
+	name: string
+	image: string
+	enableService: bool | *false
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: {
+		name: strings.ToLower(parameter.name)
+		labels: {
+			app: parameter.name
+			version: "v1"
+		}
+	}
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+if parameter.enableService {
+	outputs: service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: parameter.name + "-svc"
+		spec: {
+			selector: app: parameter.name
+			ports: [{port: 80, targetPort: 8080}]
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, componentDef)).Should(Succeed())
+		})
+
+		It("Should reject TraitDefinition with mixed valid and invalid outputs", func() {
+			traitDef := &v1beta1.TraitDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "TraitDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-trait-mixed-validity",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.TraitDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	replicas: int | *2
+	cpu: string | *"100m"
+	memory: string | *"128Mi"
+}
+
+outputs: {
+	// Valid resources
+	service: {
+		apiVersion: "v1"
+		kind: "Service"
+		metadata: name: context.name + "-svc"
+		spec: {
+			selector: app: context.name
+			ports: [{
+				port: 80
+				targetPort: 8080
+			}]
+		}
+	}
+	configmap: {
+		apiVersion: "v1"
+		kind: "ConfigMap"
+		metadata: name: context.name + "-config"
+		data: {
+			cpu: parameter.cpu
+			memory: parameter.memory
+		}
+	}
+	// Invalid CRD - should cause failure
+	customMonitor: {
+		apiVersion: "monitoring.custom.io/v1alpha1"
+		kind: "ServiceMonitor"
+		metadata: name: context.name + "-monitor"
+		spec: {
+			selector: {
+				matchLabels: {
+					app: context.name
+				}
+			}
+			endpoints: [{
+				port: "metrics"
+				interval: "30s"
+			}]
+		}
+	}
+	// Non-K8s object (should be ignored)
+	metadata: {
+		version: "1.0.0"
+		features: ["monitoring", "scaling"]
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, traitDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept PolicyDefinition with only standard Kubernetes resources", func() {
+			policyDef := &v1beta1.PolicyDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "PolicyDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-policy-standard-resources",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.PolicyDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	namespace: string
+	enabled: bool | *true
+	labels: {...} | *{}
+}
+
+output: {
+	apiVersion: "v1"
+	kind: "Namespace"
+	metadata: {
+		name: parameter.namespace
+		labels: parameter.labels
+	}
+}
+
+outputs: {
+	networkPolicy: {
+		apiVersion: "networking.k8s.io/v1"
+		kind: "NetworkPolicy"
+		metadata: {
+			name: parameter.namespace + "-default-deny"
+			namespace: parameter.namespace
+		}
+		spec: {
+			podSelector: {}
+			policyTypes: ["Ingress", "Egress"]
+		}
+	}
+	resourceQuota: {
+		apiVersion: "v1"
+		kind: "ResourceQuota"
+		metadata: {
+			name: parameter.namespace + "-quota"
+			namespace: parameter.namespace
+		}
+		spec: {
+			hard: {
+				"requests.cpu": "4"
+				"requests.memory": "8Gi"
+				"limits.cpu": "8"
+				"limits.memory": "16Gi"
+				"pods": "10"
+			}
+		}
+	}
+	limitRange: {
+		apiVersion: "v1"
+		kind: "LimitRange"
+		metadata: {
+			name: parameter.namespace + "-limits"
+			namespace: parameter.namespace
+		}
+		spec: {
+			limits: [{
+				type: "Container"
+				default: {
+					cpu: "100m"
+					memory: "128Mi"
+				}
+				defaultRequest: {
+					cpu: "50m"
+					memory: "64Mi"
+				}
+			}]
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, policyDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, policyDef)).Should(Succeed())
+		})
+
+		It("Should validate definitions with empty outputs", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-empty-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	image: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: parameter.name
+				image: parameter.image
+			}]
+		}
+	}
+}
+
+// Empty outputs should be valid
+outputs: {}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, componentDef)).Should(Succeed())
+		})
+
+		It("Should reject WorkflowStepDefinition with non-existent CRD in outputs", func() {
+			workflowStepDef := &v1beta1.WorkflowStepDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "WorkflowStepDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-workflowstep-with-invalid-output",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.WorkflowStepDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	namespace: string
+	name: string
+}
+
+output: {
+	apiVersion: "v1"
+	kind: "ConfigMap"
+	metadata: {
+		name: parameter.name
+		namespace: parameter.namespace
+	}
+	data: {
+		status: "created"
+	}
+}
+
+outputs: {
+	invalidStep: {
+		apiVersion: "workflow.custom.io/v1alpha1"
+		kind: "WorkflowStep"
+		metadata: name: parameter.name + "-step"
+		spec: {
+			type: "custom"
+			enabled: true
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, workflowStepDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept WorkflowStepDefinition with valid resources", func() {
+			workflowStepDef := &v1beta1.WorkflowStepDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "WorkflowStepDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-workflowstep-with-valid-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.WorkflowStepDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	namespace: string
+	name: string
+	data: {...}
+}
+
+output: {
+	apiVersion: "v1"
+	kind: "ConfigMap"
+	metadata: {
+		name: parameter.name
+		namespace: parameter.namespace
+	}
+	data: parameter.data
+}
+
+outputs: {
+	secret: {
+		apiVersion: "v1"
+		kind: "Secret"
+		metadata: {
+			name: parameter.name + "-secret"
+			namespace: parameter.namespace
+		}
+		type: "Opaque"
+		data: {
+			key: "dmFsdWU="  // base64 encoded "value"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, workflowStepDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, workflowStepDef)).Should(Succeed())
+		})
+
+		It("Should reject WorkflowStepDefinition with mixed valid and invalid resources", func() {
+			workflowStepDef := &v1beta1.WorkflowStepDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "WorkflowStepDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-workflowstep-mixed-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.WorkflowStepDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+	namespace: string
+}
+
+output: {
+	apiVersion: "batch/v1"
+	kind: "Job"
+	metadata: {
+		name: parameter.name
+		namespace: parameter.namespace
+	}
+	spec: {
+		template: {
+			spec: {
+				containers: [{
+					name: "job"
+					image: "busybox"
+					command: ["echo", "hello"]
+				}]
+				restartPolicy: "Never"
+			}
+		}
+	}
+}
+
+outputs: {
+	// Valid resource
+	cronJob: {
+		apiVersion: "batch/v1"
+		kind: "CronJob"
+		metadata: {
+			name: parameter.name + "-cron"
+			namespace: parameter.namespace
+		}
+		spec: {
+			schedule: "*/5 * * * *"
+			jobTemplate: {
+				spec: {
+					template: {
+						spec: {
+							containers: [{
+								name: "cron"
+								image: "busybox"
+								command: ["echo", "cron"]
+							}]
+							restartPolicy: "Never"
+						}
+					}
+				}
+			}
+		}
+	}
+	// Invalid custom resource
+	customScheduler: {
+		apiVersion: "scheduler.custom.io/v1beta1"
+		kind: "CustomScheduler"
+		metadata: {
+			name: parameter.name + "-scheduler"
+		}
+		spec: {
+			interval: "5m"
+			action: "notify"
+		}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, workflowStepDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+
+		It("Should accept WorkflowStepDefinition without outputs", func() {
+			workflowStepDef := &v1beta1.WorkflowStepDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "WorkflowStepDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-workflowstep-no-outputs",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.WorkflowStepDefinitionSpec{
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	message: string
+	delay: int | *1
+}
+
+// This workflow step just processes data without creating resources
+processedData: {
+	message: parameter.message
+	timestamp: "2024-01-01T00:00:00Z"
+	processed: true
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, workflowStepDef)
+			Expect(err).NotTo(HaveOccurred())
+
+			// Clean up
+			Expect(k8sClient.Delete(ctx, workflowStepDef)).Should(Succeed())
+		})
+
+		It("Should reject ComponentDefinition with invalid apiVersion format", func() {
+			componentDef := &v1beta1.ComponentDefinition{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ComponentDefinition",
+					APIVersion: "core.oam.dev/v1beta1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "test-comp-invalid-apiversion",
+					Namespace: namespace,
+				},
+				Spec: v1beta1.ComponentDefinitionSpec{
+					Workload: common.WorkloadTypeDescriptor{
+						Definition: common.WorkloadGVK{
+							APIVersion: "apps/v1",
+							Kind:       "Deployment",
+						},
+					},
+					Schematic: &common.Schematic{
+						CUE: &common.CUE{
+							Template: `
+parameter: {
+	name: string
+}
+
+output: {
+	apiVersion: "apps/v1"
+	kind: "Deployment"
+	metadata: name: parameter.name
+	spec: {
+		selector: matchLabels: app: parameter.name
+		template: {
+			metadata: labels: app: parameter.name
+			spec: containers: [{
+				name: "nginx"
+				image: "nginx:latest"
+			}]
+		}
+	}
+}
+
+outputs: {
+	invalidResource: {
+		apiVersion: "invalid-format-no-slash"
+		kind: "SomeResource"
+		metadata: name: parameter.name
+		spec: {}
+	}
+}`,
+						},
+					},
+				},
+			}
+
+			err := k8sClient.Create(ctx, componentDef)
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(ContainSubstring("resource type not found on cluster"))
+		})
+	})
+})
diff --git a/test/e2e-test/definition_revision_test.go b/test/e2e-test/definition_revision_test.go
index 63db5e17..b437011e 100644
--- a/test/e2e-test/definition_revision_test.go
+++ b/test/e2e-test/definition_revision_test.go
@@ -930,7 +930,7 @@ outputs: service: {
 }
 
 outputs: ingress: {
-	apiVersion: "networking.k8s.io/v1beta1"
+	apiVersion: "networking.k8s.io/v1"
 	kind:       "Ingress"
 	metadata:
 		name: context.name
@@ -941,9 +941,14 @@ outputs: ingress: {
 				paths: [
 					for k, v in parameter.http {
 						path: k
+						pathType: "Prefix"
 						backend: {
-							serviceName: context.name
-							servicePort: v
+							service: {
+								name: context.name
+								port: {
+									number: v
+								}
+							}
 						}
 					},
 				]