> ## Documentation Index
> Fetch the complete documentation index at: https://docs.varianz.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CEL reference

> The expression language for Varianz stages: args and ctx namespaces, invoke() and sample() builtins, supported syntax, optionals, and current limitations.

Varianz stages are written in **CEL** (Common Expression Language) — sandboxed, deterministic, and safe to push to running services. Expressions compile against the target VPoint's schema when inserted, so type and field errors surface at insert time.

## Namespaces

**`args` — the VPoint's inputs.** Parameters are reachable *only* through `args`:

```cel theme={null}
args.user.age >= 18        // ✅ the parameter `user`
user.age >= 18             // ❌ rejected at parse time
$.user.age >= 18           // ❌ `$` is not a CEL root
```

Parameter names follow the SDK's schema naming (for example Go's `BasePrice` is `args.base_price` — see each SDK's field-naming rules).

**`ctx` — context functions** registered by the service: `ctx.applyTax(invoke(), 0.08)`. See [context functions](/guides/instrument-a-service#context-functions).

## Varianz builtins

### `invoke()`

Executes the next stage in the pipeline — or, at the end of the chain, the real function — and returns its result.

* `invoke()` with **zero arguments** forwards the original arguments.
* `invoke(a, b, ...)` with **exactly the VPoint's parameter count** substitutes different arguments.
* Any other arity is a compile error.

An expression *without* `invoke()` fully replaces the function's result; the real code never runs.

### `sample(name, value)`

Captures `value` under the label `name` for the test session to assert on, and returns `value` unchanged. Exactly two arguments. Because it's transparent, it composes anywhere:

```cel theme={null}
sample("charge-result", invoke())                       // probe the real result
let amt = sample("amount", args.input.amount);          // capture an input,
amt > 1000.0 ? ChargeResult { declined: true } : invoke()   // then branch on it
```

## Struct construction

```cel theme={null}
ChargeResult { txnId: "test-001", declined: false }
```

* Type names must match the schema exactly and are **case-sensitive**. Use the simple name (`ChargeResult`), never a package-qualified one (`pb.ChargeResult`, `hipstershop.ChargeResult`).
* All required fields must be provided.
* Nested structs work inline: `GetProductOutput { id: "X", price: Money { currency_code: "USD", units: 42, nanos: 0 } }`.

## Supported syntax

| Construct                       | Example                                                                                                                                       | Notes                                              |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| Literals                        | `123`, `0xFF`, `3.14`, `"str"`, `'str'`, `true`, `null`                                                                                       |                                                    |
| Arithmetic, comparison, logical | `a + b`, `a == b`, `a && b`, `!x`, `-x`                                                                                                       |                                                    |
| Ternary                         | `cond ? then : else`                                                                                                                          |                                                    |
| Struct literal                  | `Type { field: value }`                                                                                                                       | Validated against the schema                       |
| List / map literals             | `[a, b, c]`, `{"k": v}`                                                                                                                       |                                                    |
| Indexing                        | `arr[0]`, `map["key"]`, `tuple[0]`                                                                                                            |                                                    |
| Field presence                  | `has(e.f)`                                                                                                                                    |                                                    |
| Membership                      | `a in b`                                                                                                                                      |                                                    |
| List predicates                 | `list.exists(v, pred)`, `list.all(v, pred)`                                                                                                   | `@` refers to the current element in lambda bodies |
| Let bindings                    | `let x = expr; body`                                                                                                                          | `;`-separated                                      |
| Optionals                       | `value.?field`, `value[?key]`, `optional.of(v)`, `optional.none()`, `opt.hasValue()`, `opt.value()`, `opt.or(other)`, `opt.orValue(fallback)` | `or`/`orValue` evaluate the fallback lazily        |
| Method / namespace calls        | `expr.method(args)`, `ns.func(args)`                                                                                                          |                                                    |
| Comments                        | `// comment`                                                                                                                                  |                                                    |

Tuple values expose synthetic positional fields: read `pair._0` (or `pair[0]` when the schema proves a fixed tuple), construct with `Pair { _0: "north", _1: 8 }`. Generated wrapper names may be lowercase.

## Optionals and `null` at the stage boundary

During evaluation, CEL keeps the standard distinction — `optional.of(null).hasValue()` is `true`, `optional.none().hasValue()` is `false`. At the stage boundary, however, Varianz transports an optional as either its inner value or *absent*: both `optional.of(null)` and `optional.none()` materialize as absence to the SDK on the other side.

## Not yet supported

* Comprehensions `map`, `filter`, `exists_one` (only `exists`/`all`)
* Type conversions (`int()`, `string()`, …) and date/time functions
* Error diagnostics carry byte offsets, not line/column

## Where CEL runs

CEL stages attach to a VPoint at an [anchor](/concepts/stages#the-pipeline) — **`MID` by default** — scoped to your [session](/concepts/sessions). Insert them from any test SDK (`cel(target, expression)`); target syntax and routing filters are in [Naming and routing](/reference/naming-and-routing).
