Repository: kubernetes-sigs/kro

Issue:
Kro is not supporting CEL raw string 
### Description

**Observed Behavior**:

The Pulumi YAML support uses ${...} to evaluate expressions inside the `yaml`. When using this with we get following issue in our `rgd`: 

```bash
failed to validate resource CEL expressions: failed to dry-run expression project.id: failed to compile expression: ERROR: <input>:1:1: undeclared reference to 'project' (in container '')
```

Have a look into the `projectId` property. 


```yaml
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: my-pko-app
spec:
  schema:
    apiVersion: v1alpha1
    kind: MyApplication
    spec:
      name: string
      stack: string
      gitRepo: string
      region: string | default="nyc1"
      instanceCount: integer | default=1
      instanceSizeSlug: string | default="apps-s-1vcpu-1gb"
  resources:
  - id: program
    template:
      apiVersion: pulumi.com/v1
      kind: Program
      metadata:
        name: ${schema.spec.name}
      program:
        resources:
          project:
            type: digitalocean:Project
            properties:
              name: ${schema.spec.name}-project
              description: A project to represent development resources.
              purpose: Web Application
              environment: Development
          app:
            type: digitalocean:App
            properties:
              projectId: ${project.id}
              spec:
                name: app-${schema.spec.name}-${schema.spec.region}
                region: ${schema.spec.region}
                services:
                - name: app-service-${schema.spec.name}-${schema.spec.region}
                  instanceCount: ${schema.spec.instanceCount}
                  instanceSizeSlug: ${schema.spec.instanceSizeSlug}
                  git:
                    repoCloneUrl: ${schema.spec.gitRepo}
                    branch: "main"
  - id: stack
    template:
      apiVersion: pulumi.com/v1
      kind: Stack
      metadata:
        name: ${schema.spec.name}
      spec:
        stack: ${schema.spec.stack}
        envRefs:
          DIGITALOCEAN_TOKEN:
            type: Env
            env:
              name: DIGITALOCEAN_TOKEN
        programRef:
          name: ${schema.spec.name}
        destroyOnFinalize: true
```

More details about the Pulumi Property Access: https://www.pulumi.com/docs/iac/languages-sdks/yaml/yaml-language-reference/#property-access


To avoid this, I want to use the `raw` string support of CEL: https://github.com/google/cel-spec/blob/master/doc/langdef.md and "tell" kro to ignore the ${...} expression and leave it to the Pulumi Operator to do the work.

But this is not working.

**Expected Behavior**:

Use R or r to support raw strings in kro

**Reproduction Steps** (Please include `ResourceGraphDefinition` and `Instances` files):

```yaml
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: my-pko-app
spec:
  schema:
    apiVersion: v1alpha1
    kind: MyApplication
    spec:
      name: string
      stack: string
      gitRepo: string
      region: string | default="nyc1"
      instanceCount: integer | default=1
      instanceSizeSlug: string | default="apps-s-1vcpu-1gb"
  resources:
  - id: program
    template:
      apiVersion: pulumi.com/v1
      kind: Program
      metadata:
        name: ${schema.spec.name}
      program:
        resources:
          project:
            type: digitalocean:Project
            properties:
              name: ${schema.spec.name}-project
              description: A project to represent development resources.
              purpose: Web Application
              environment: Development
          app:
            type: digitalocean:App
            properties:
              projectId: R"${project.id}"
              spec:
                name: app-${schema.spec.name}-${schema.spec.region}
                region: ${schema.spec.region}
                services:
                - name: app-service-${schema.spec.name}-${schema.spec.region}
                  instanceCount: ${schema.spec.instanceCount}
                  instanceSizeSlug: ${schema.spec.instanceSizeSlug}
                  git:
                    repoCloneUrl: ${schema.spec.gitRepo}
                    branch: "main"
  - id: stack
    template:
      apiVersion: pulumi.com/v1
      kind: Stack
      metadata:
        name: ${schema.spec.name}
      spec:
        stack: ${schema.spec.stack}
        envRefs:
          DIGITALOCEAN_TOKEN:
            type: Env
            env:
              name: DIGITALOCEAN_TOKEN
        programRef:
          name: ${schema.spec.name}
        destroyOnFinalize: true
```

```yaml
apiVersion: kro.run/v1alpha1
kind: MyApplication
metadata:
  name: my-awsome-pko-based-app
spec:
  name: my-awesome-app
  stack: ediri/my-awesome-app/dev
  gitRepo: https://github.com/digitalocean/sample-vuejs
```


**Versions**:
- kro version: 0.2.1
- Kubernetes Version (`kubectl version`):  v1.31.1

**Involved Controllers**:
- Controller URLs and Versions (if applicable):

**Error Logs** (if applicable)**:
```
failed to validate resource CEL expressions: failed to dry-run expression project.id: failed to compile expression: ERROR: <input>:1:1: undeclared reference to 'project' (in container '')
```

* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request
* Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
* If you are interested in working on this issue or have submitted a pull request, please leave a comment


### Which option describes the most your issue?

_No response_

Discussion:
@dirien: I continued on this and I could get it worked using following workaround `${"$"+"project.id"}`. I needed to split this, because if I do not split it in two seperate strings and concat it later I run into this code. https://github.com/kro-run/kro/blob/b09909e00a2bb39cc327e93610409268b3f7106f/pkg/graph/parser/cel.go#L61-L64

I created a PR #297 to allow "nested expression" only when they are escaped and treated as string literal@a-hilaly: @dirien thank you for your PR again, we will include the patch in 0.2.3 or 0.3, which ever happens to be released next. Meanwhile you can always pin to one of recent kro controller images => https://github.com/kro-run/kro/pkgs/container/kro%2Fcontroller@k8s-triage-robot: The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:
- After 90d of inactivity, `lifecycle/stale` is applied
- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied
- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed

You can:
- Mark this issue as fresh with `/remove-lifecycle stale`
- Close this issue with `/close`
- Offer to help out with [Issue Triage][1]

Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community).

/lifecycle stale

[1]: https://www.kubernetes.dev/docs/guide/issue-triage/

Solution patch:
diff --git a/.github/workflows/build-push-image.yaml b/.github/workflows/build-push-image.yaml
index 69cdc91..4fa4d3c 100644
--- a/.github/workflows/build-push-image.yaml
+++ b/.github/workflows/build-push-image.yaml
@@ -4,8 +4,16 @@ on:
   push:
     branches: [ main ]
     tags: [ 'v*' ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
   pull_request:
     branches: [ main ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
 
 env:
   KO_DOCKER_REPO: ghcr.io/${{ github.repository }}/controller
diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml
index 9fb8a02..9f8c358 100644
--- a/.github/workflows/golangci-lint.yaml
+++ b/.github/workflows/golangci-lint.yaml
@@ -3,9 +3,13 @@ on:
   push:
     branches:
       - main
+    paths:
+      - "**.go"
   pull_request:
     branches:
       - main
+    paths:
+      - "**.go"
 
 permissions:
   contents: read
diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml
index 4c31242..0430758 100644
--- a/.github/workflows/integration-tests.yaml
+++ b/.github/workflows/integration-tests.yaml
@@ -3,8 +3,16 @@ name: Integration Tests
 on:
   push:
     branches: [ main ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
   pull_request:
     branches: [ main ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
 
 jobs:
   integration:
diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml
index 90dfb08..a91b542 100644
--- a/.github/workflows/unit-tests.yaml
+++ b/.github/workflows/unit-tests.yaml
@@ -2,8 +2,16 @@ name: unit tests
 on:
   push:
     branches: [ main ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
   pull_request:
     branches: [ main ]
+    paths:
+      - "**.go"
+      - go.mod
+      - go.sum
 
 jobs:
   build:
diff --git a/Makefile b/Makefile
index e6a4876..9580572 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,6 @@
-
-GIT_COMMIT ?= $(shell git rev-parse --short HEAD)
-BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
-RELEASE_VERSION ?= dev-$(GIT_COMMIT)
+export GIT_COMMIT ?= $(shell git rev-parse --short HEAD)
+export BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
+export RELEASE_VERSION ?= dev-$(GIT_COMMIT)
 OCI_REPO ?= ghcr.io/kro-run/kro
 
 HELM_IMAGE ?= ${OCI_REPO}
@@ -237,7 +236,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified
 
 .PHONY: deploy-kind
 deploy-kind: export KO_DOCKER_REPO=kind.local
-deploy-kind:
+deploy-kind: ko
 	$(KIND) delete clusters ${KIND_CLUSTER_NAME} || true
 	$(KIND) create cluster --name ${KIND_CLUSTER_NAME}
 	$(KUBECTL) --context kind-$(KIND_CLUSTER_NAME) create namespace kro-system
diff --git a/go.mod b/go.mod
index 7eccb90..9aade2a 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.24.0
 require (
 	github.com/go-logr/logr v1.4.2
 	github.com/gobuffalo/flect v1.0.2
-	github.com/google/cel-go v0.22.0
+	github.com/google/cel-go v0.24.1
 	github.com/onsi/ginkgo/v2 v2.20.0
 	github.com/onsi/gomega v1.34.1
 	github.com/prometheus/client_golang v1.19.1
@@ -25,7 +25,7 @@ require (
 )
 
 require (
-	cel.dev/expr v0.18.0 // indirect
+	cel.dev/expr v0.19.1 // indirect
 	github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index 6e16f85..7235610 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
-cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
+cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
+cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
 github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
 github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -41,8 +41,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
-github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
+github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI=
+github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
 github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
diff --git a/pkg/cel/ast/inspector.go b/pkg/cel/ast/inspector.go
index de9666e..d0e89ef 100644
--- a/pkg/cel/ast/inspector.go
+++ b/pkg/cel/ast/inspector.go
@@ -493,7 +493,7 @@ func (a *Inspector) structExpressionToString(s *exprpb.Expr_CreateStruct) string
 }
 
 func isInternalIdentifier(name string) bool {
-	return name == "__result__" || strings.HasPrefix(name, "$$")
+	return name == "@result" || strings.HasPrefix(name, "$$")
 }
 
 func isInternalFunction(name string) bool {
diff --git a/pkg/graph/parser/cel.go b/pkg/graph/parser/cel.go
index 907ff53..910bba2 100644
--- a/pkg/graph/parser/cel.go
+++ b/pkg/graph/parser/cel.go
@@ -42,26 +42,48 @@ func extractExpressions(str string) ([]string, error) {
 		// Adjust the start index to the actual position in the string
 		startIdx += start
 
-		// We need to find the matching end bracket. we have to be careful about
-		// nested expressions and dictionary building expressions. For example:
-		// a user can have an expression like "${{"key": 123}}". For this reason,
-		// we need to keep track of the bracket count and make sure that we only
-		// consider the outermost expression
+		// We need to find the matching end bracket, being careful about
+		// nested expressions, dictionary building expressions, and string literals
 		bracketCount := 1
 		endIdx := startIdx + len(exprStart)
+		inStringLiteral := false
+		escapeNext := false
+
 		for endIdx < len(str) {
-			if str[endIdx] == '{' {
-				bracketCount++
-			} else if str[endIdx] == '}' {
-				bracketCount--
-				// If we have reached the end of the expression, break
-				if bracketCount == 0 {
-					break
+			c := str[endIdx]
+
+			// Handle escape sequences inside string literals
+			if escapeNext {
+				escapeNext = false
+				endIdx++
+				continue
+			}
+
+			// Check for escape character inside string literals
+			if inStringLiteral && c == '\\' {
+				escapeNext = true
+				endIdx++
+				continue
+			}
+
+			// Handle string literal boundaries
+			if c == '"' {
+				inStringLiteral = !inStringLiteral
+			} else if !inStringLiteral {
+				// Only count braces when not inside a string literal
+				if c == '{' {
+					bracketCount++
+				} else if c == '}' {
+					bracketCount--
+					if bracketCount == 0 {
+						break
+					}
+				} else if endIdx+1 < len(str) && str[endIdx:endIdx+2] == "${" {
+					// Allow nested expressions, but only if they are escaped with quotes
+					if str[endIdx-1] != '"' {
+						return nil, ErrNestedExpression
+					}
 				}
-			} else if endIdx+1 < len(str) && str[endIdx:endIdx+2] == "${" {
-				// We do not allow nested expressions. I'm not sure if this is a
-				// good idea, but its sounds like a reasonable restriction.
-				return nil, ErrNestedExpression
 			}
 			endIdx++
 		}
diff --git a/pkg/graph/resource.go b/pkg/graph/resource.go
index 8c39c0e..e2c6fb3 100644
--- a/pkg/graph/resource.go
+++ b/pkg/graph/resource.go
@@ -21,7 +21,6 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/kube-openapi/pkg/validation/spec"
 
-	rgschema "github.com/kro-run/kro/pkg/graph/schema"
 	"github.com/kro-run/kro/pkg/graph/variable"
 )
 
@@ -154,11 +153,6 @@ func (r *Resource) GetIncludeWhenExpressions() []string {
 	return r.includeWhenExpressions
 }
 
-// GetTopLevelFields returns the top-level fields of the resource.
-func (r *Resource) GetTopLevelFields() []string {
-	return rgschema.GetResourceTopLevelFieldNames(r.schema)
-}
-
 // IsNamespaced returns true if the resource is namespaced.
 func (r *Resource) IsNamespaced() bool {
 	return r.namespaced
diff --git a/pkg/graph/schema/schema.go b/pkg/graph/schema/schema.go
deleted file mode 100644
index beaf179..0000000
--- a/pkg/graph/schema/schema.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2025 The Kube Resource Orchestrator Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//	http://www.apache.org/licenses/LICENSE-2.0
-//
-// or in the "license" file accompanying this file. This file 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 schema
-
-import (
-	"slices"
-
-	"k8s.io/kube-openapi/pkg/validation/spec"
-)
-
-// GetResourceTopLevelFieldNames returns the top level field names
-// of the resource. It walks through the schema of the resource and
-// retrieves the top level fields including spec, status, metadata,
-// etc.
-//
-// It is up to the caller to sort filter the field names they want.
-func GetResourceTopLevelFieldNames(schema *spec.Schema) []string {
-	fieldNames := []string{}
-	if schema == nil || schema.Properties == nil {
-		return fieldNames
-	}
-	for fieldName := range schema.Properties {
-		if fieldName != "apiVersion" && fieldName != "kind" {
-			fieldNames = append(fieldNames, fieldName)
-		}
-	}
-
-	slices.Sort(fieldNames)
-	return fieldNames
-}
diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go
index 281d9fe..21e3fda 100644
--- a/pkg/runtime/interfaces.go
+++ b/pkg/runtime/interfaces.go
@@ -109,9 +109,6 @@ type ResourceDescriptor interface {
 	// be evaluated before deciding whether to create a resource
 	GetIncludeWhenExpressions() []string
 
-	// GetTopLevelFields returns the list of top-level fields in the resource.
-	// e.g spec, status, metadata, etc.
-	GetTopLevelFields() []string
 
 	// IsNamespaced returns true if the resource is namespaced, and false if it's
 	// cluster-scoped.



Test patch:
diff --git a/pkg/graph/crd/crd_test.go b/pkg/graph/crd/crd_test.go
new file mode 100644
index 0000000..87bcddc
--- /dev/null
+++ b/pkg/graph/crd/crd_test.go
@@ -0,0 +1,236 @@
+// Copyright 2025 The Kube Resource Orchestrator Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"). You may
+// not use this file except in compliance with the License. A copy of the
+// License is located at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// or in the "license" file accompanying this file. This file 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 crd
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+
+	"github.com/kro-run/kro/api/v1alpha1"
+)
+
+func TestSynthesizeCRD(t *testing.T) {
+	tests := []struct {
+		name                 string
+		group                string
+		apiVersion           string
+		kind                 string
+		spec                 extv1.JSONSchemaProps
+		status               extv1.JSONSchemaProps
+		statusFieldsOverride bool
+		expectedName         string
+		expectedGroup        string
+	}{
+		{
+			name:                 "standard group and kind",
+			group:                "kro.com",
+			apiVersion:           "v1",
+			kind:                 "Widget",
+			spec:                 extv1.JSONSchemaProps{Type: "object"},
+			status:               extv1.JSONSchemaProps{Type: "object"},
+			statusFieldsOverride: true,
+			expectedName:         "widgets.kro.com",
+			expectedGroup:        "kro.com",
+		},
+		{
+			name:                 "empty group uses default domain",
+			group:                "",
+			apiVersion:           "v1alphav2",
+			kind:                 "Service",
+			spec:                 extv1.JSONSchemaProps{Type: "object"},
+			status:               extv1.JSONSchemaProps{Type: "object"},
+			statusFieldsOverride: false,
+			expectedName:         "services." + v1alpha1.KRODomainName,
+			expectedGroup:        v1alpha1.KRODomainName,
+		},
+		{
+			name:                 "mixes case kind",
+			group:                "kro.com",
+			apiVersion:           "v2",
+			kind:                 "DataBase",
+			spec:                 extv1.JSONSchemaProps{Type: "object"},
+			status:               extv1.JSONSchemaProps{Type: "object"},
+			statusFieldsOverride: true,
+			expectedName:         "databases.kro.com",
+			expectedGroup:        "kro.com",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			crd := SynthesizeCRD(tt.group, tt.apiVersion, tt.kind, tt.spec, tt.status, tt.statusFieldsOverride)
+
+			assert.Equal(t, tt.expectedName, crd.Name)
+			assert.Equal(t, tt.expectedGroup, crd.Spec.Group)
+			assert.Equal(t, tt.kind, crd.Spec.Names.Kind)
+			assert.Equal(t, tt.kind+"List", crd.Spec.Names.ListKind)
+
+			require.Len(t, crd.Spec.Versions, 1)
+			version := crd.Spec.Versions[0]
+			assert.Equal(t, tt.apiVersion, version.Name)
+			assert.True(t, version.Served)
+			assert.True(t, version.Storage)
+
+			require.NotNil(t, version.Schema)
+			require.NotNil(t, version.Schema.OpenAPIV3Schema)
+
+			require.NotNil(t, version.Subresources)
+			require.NotNil(t, version.Subresources.Status)
+		})
+	}
+}
+
+func TestNewCRD(t *testing.T) {
+	tests := []struct {
+		name             string
+		group            string
+		apiVersion       string
+		kind             string
+		expectedName     string
+		expectedKind     string
+		expectedPlural   string
+		expectedSingular string
+	}{
+		{
+			name:             "basic example",
+			group:            "kro.com",
+			apiVersion:       "v1",
+			kind:             "Test",
+			expectedName:     "tests.kro.com",
+			expectedKind:     "Test",
+			expectedPlural:   "tests",
+			expectedSingular: "test",
+		},
+		{
+			name:             "uppercase kind",
+			group:            "kro.com",
+			apiVersion:       "v2beta1",
+			kind:             "CONFIG",
+			expectedName:     "configs.kro.com",
+			expectedKind:     "CONFIG",
+			expectedPlural:   "configs",
+			expectedSingular: "config",
+		},
+		{
+			name:             "mixed case kind",
+			group:            "kro.com",
+			apiVersion:       "v2beta1",
+			kind:             "WebHook",
+			expectedName:     "webhooks.kro.com",
+			expectedKind:     "WebHook",
+			expectedPlural:   "webhooks",
+			expectedSingular: "webhook",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			schema := &extv1.JSONSchemaProps{Type: "object"}
+			crd := newCRD(tt.group, tt.apiVersion, tt.kind, schema)
+
+			assert.Equal(t, tt.expectedName, crd.Name)
+			assert.Equal(t, tt.group, crd.Spec.Group)
+			assert.Equal(t, tt.expectedKind, crd.Spec.Names.Kind)
+			assert.Equal(t, tt.expectedKind+"List", crd.Spec.Names.ListKind)
+			assert.Equal(t, tt.expectedPlural, crd.Spec.Names.Plural)
+			assert.Equal(t, tt.expectedSingular, crd.Spec.Names.Singular)
+
+			assert.Equal(t, extv1.NamespaceScoped, crd.Spec.Scope)
+
+			require.Len(t, crd.Spec.Versions, 1)
+			assert.Equal(t, tt.apiVersion, crd.Spec.Versions[0].Name)
+			assert.Equal(t, schema, crd.Spec.Versions[0].Schema.OpenAPIV3Schema)
+
+			assert.Nil(t, crd.OwnerReferences)
+		})
+	}
+}
+
+func TestNewCRDSchema(t *testing.T) {
+	tests := []struct {
+		name                    string
+		spec                    extv1.JSONSchemaProps
+		status                  extv1.JSONSchemaProps
+		statusFieldsOverride    bool
+		expectedStateField      bool
+		expectedConditionsField bool
+	}{
+		{
+			name:                    "with override enabled and empty status",
+			spec:                    extv1.JSONSchemaProps{Type: "object"},
+			status:                  extv1.JSONSchemaProps{Type: "object"},
+			statusFieldsOverride:    true,
+			expectedStateField:      true,
+			expectedConditionsField: true,
+		},
+		{
+			name:                    "with override disabled",
+			spec:                    extv1.JSONSchemaProps{Type: "object"},
+			status:                  extv1.JSONSchemaProps{Type: "object"},
+			statusFieldsOverride:    false,
+			expectedStateField:      false,
+			expectedConditionsField: false,
+		},
+		{
+			name: "with existing status properties and override enabled",
+			spec: extv1.JSONSchemaProps{Type: "object"},
+			status: extv1.JSONSchemaProps{Type: "object", Properties: map[string]extv1.JSONSchemaProps{
+				"state": {
+					Type:        "string",
+					Description: "Custom state filed",
+				},
+				"customField": {Type: "string"},
+			}},
+			statusFieldsOverride:    true,
+			expectedStateField:      true,
+			expectedConditionsField: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			schema := newCRDSchema(tt.spec, tt.status, tt.statusFieldsOverride)
+
+			assert.Equal(t, "object", schema.Type)
+			require.NotNil(t, schema.Properties)
+
+			assert.Contains(t, schema.Properties, "apiVersion")
+			assert.Contains(t, schema.Properties, "kind")
+			assert.Contains(t, schema.Properties, "metadata")
+			assert.Contains(t, schema.Properties, "spec")
+			assert.Contains(t, schema.Properties, "status")
+
+			assert.Equal(t, tt.spec, schema.Properties["spec"])
+
+			statusProps := schema.Properties["status"]
+			require.NotNil(t, statusProps.Properties)
+
+			if tt.expectedStateField {
+				assert.Contains(t, statusProps.Properties, "state")
+				assert.Equal(t, defaultConditionsType, statusProps.Properties["conditions"])
+			}
+
+			if tt.status.Properties != nil {
+				if customField, exists := tt.status.Properties["customField"]; exists {
+					assert.Contains(t, statusProps.Properties, "customField")
+					assert.Equal(t, customField, statusProps.Properties["customField"])
+				}
+			}
+
+		})
+	}
+}
diff --git a/pkg/graph/parser/cel_test.go b/pkg/graph/parser/cel_test.go
index 1bb10aa..10f7602 100644
--- a/pkg/graph/parser/cel_test.go
+++ b/pkg/graph/parser/cel_test.go
@@ -114,6 +114,24 @@ func TestExtractExpressions(t *testing.T) {
 			want:    nil,
 			wantErr: true,
 		},
+		{
+			name:    "Nested expression but with quotes",
+			input:   "${outer(\"${inner}\")}",
+			want:    []string{"outer(\"${inner}\")"},
+			wantErr: false,
+		},
+		{
+			name:    "Nested closing brace without opening one",
+			input:   "${\"text with }} inside\"}",
+			want:    []string{"\"text with }} inside\""},
+			wantErr: false,
+		},
+		{
+			name:    "Nested open brace without closing one",
+			input:   "${\"text with { inside\"}",
+			want:    []string{"\"text with { inside\""},
+			wantErr: false,
+		},
 		{
 			name:    "Expressions with dictionary building",
 			input:   "${true ? {'key': 'value'} : {'key': 'value2'}}",
@@ -197,6 +215,9 @@ func TestIsOneShotExpression(t *testing.T) {
 		{"With newlines", "${resource.list.map(\n  x,\n  x * 2\n)}", true, false},
 		{"Complex expression", "${resource.list.map(x, x.field).filter(y, y > 5)}", true, false},
 		{"Nested expression (should error)", "${outer(${inner})}", false, true},
+		{"Nested expression but with quotes", "${outer(\"${inner}\")}", true, false},
+		{"Nested closing brace without opening one", "${\"text with }} inside\"}", true, false},
+		{"Nested open brace without closing one", "${\"text with { inside\"}", true, false},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/graph/parser/parser_test.go b/pkg/graph/parser/parser_test.go
index bcceb08..2518af6 100644
--- a/pkg/graph/parser/parser_test.go
+++ b/pkg/graph/parser/parser_test.go
@@ -1196,6 +1196,18 @@ func TestOneOfWithStructuralConstraints(t *testing.T) {
 		if !reflect.DeepEqual(expressions[0].Path, expected.Path) {
 			t.Errorf("Expected path %s, got %s", expected.Path, expressions[0].Path)
 		}
+
+		if !reflect.DeepEqual(expressions[0].Expressions, expected.Expressions) {
+			t.Errorf("Expressions mismatch: got %v, want %v", expressions[0].Expressions, expected.Expressions)
+		}
+
+		if !reflect.DeepEqual(expressions[0].ExpectedTypes, expected.ExpectedTypes) {
+			t.Errorf("ExpectedTypes mismatch: got %v, want %v", expressions[0].ExpectedTypes, expected.ExpectedTypes)
+		}
+
+		if expressions[0].StandaloneExpression != expected.StandaloneExpression {
+			t.Errorf("StandaloneExpression mismatch: got %v, want %v", expressions[0].StandaloneExpression, expected.StandaloneExpression)
+		}
 	})
 
 	t.Run("networkRef with external reference", func(t *testing.T) {
@@ -1284,6 +1296,10 @@ func TestOneOfWithStructuralConstraints(t *testing.T) {
 		if !reflect.DeepEqual(expressions[0].ExpectedTypes, expected.ExpectedTypes) {
 			t.Errorf("Expected types %v, got %v", expected.ExpectedTypes, expressions[0].ExpectedTypes)
 		}
+
+		if expressions[0].StandaloneExpression != expected.StandaloneExpression {
+			t.Errorf("StandaloneExpression mismatch: got %v, want %v", expressions[0].StandaloneExpression, expected.StandaloneExpression)
+		}
 	})
 }
 
diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go
index b042935..d30b0b7 100644
--- a/pkg/runtime/runtime_test.go
+++ b/pkg/runtime/runtime_test.go
@@ -2644,9 +2644,6 @@ func (m *mockResource) GetIncludeWhenExpressions() []string {
 	return m.conditions
 }
 
-func (m *mockResource) GetTopLevelFields() []string {
-	return m.topLevelFields
-}
 
 func (m *mockResource) IsNamespaced() bool {
 	return m.namespaced