> ## 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.

# Stages

> Stages are units of behavior attached to a VPoint — CEL expressions that override results, inject failures, or capture samples, running at pipeline anchors.

A **stage** is a unit of behavior attached to a [VPoint](/concepts/vpoints). The user-facing kind is a **CEL stage**: a small expression, pushed from a test, that controls what the VPoint does for calls in your [session](/concepts/sessions).

## The pipeline

Every VPoint call runs through a fixed sequence of **anchors** — slots where stages can attach:

```
VALIDATE → PRE → MID → POST → AUDIT → default plan (your function)
```

| Anchor     | Typical use                                                                                        |
| ---------- | -------------------------------------------------------------------------------------------------- |
| `VALIDATE` | Early input checks; can reject execution before it starts                                          |
| `PRE`      | Runs before the default plan; can modify arguments or bypass entirely                              |
| `MID`      | Wraps the default plan — overrides and probes live here. **CEL stages insert at `MID` by default** |
| `POST`     | Runs after the default plan; can transform the return value                                        |
| `AUDIT`    | Always runs, success or failure — logging, metrics, side effects                                   |

A stage that produces a value short-circuits the rest of the pipeline; a stage that passes through lets execution continue. With no stages attached, the call takes a fast path straight to the default plan — the original function, exactly as written.

## The three stage patterns

Almost every test uses one of three shapes. Inside a CEL expression, `args.<param>` reads the call's inputs and `invoke()` executes the next stage or the real function.

**Override** — replace the result entirely. The real function never runs:

```java theme={null}
session.insert(cel("payment/charge",
    "ChargeResult { txnId: \"test-001\" }"));
```

**Conditional override** — intercept some calls, pass the rest through:

```java theme={null}
session.insert(cel("payment/charge",
    "args.input.amount > 1000 ? ChargeResult { declined: true } : invoke()"));
```

**Probe** — observe without changing behavior. `sample()` captures a value for the test to assert on and returns it unchanged:

```java theme={null}
session.insert(cel("payment/charge",
    "sample(\"charge-result\", invoke())"));
```

The captured sample streams back to your test session, where you assert on it:

```python theme={null}
varianz.assert_sample("charge-result") \
    .within(timedelta(seconds=10)) \
    .has_value("txnId", "test-001")
```

## Validated against the schema

CEL stages compile against the VPoint's schema when inserted. Type names, field names, and required fields are all checked — `ChargeResult { txn: ... }` fails at insert time if the field is `txnId`. This is why inspecting the schema first (via the [MCP tools](/guides/ai-assisted-testing) or your SDK's build-time output) beats guessing.

Key rules:

* Inputs are read **only** through `args.<param>` — a bare parameter name is rejected at parse time.
* Type names match the schema exactly and are case-sensitive; use the simple name (`ChargeResult`, not `pb.ChargeResult`).
* Without `invoke()`, the expression fully replaces the function's result.
* `ctx.<fn>(...)` calls [context functions](/guides/instrument-a-service#context-functions) your service registered.

The full expression language — operators, optionals, list predicates, `let` bindings, and current limitations — is in the [CEL reference](/reference/cel).

## Beyond CEL

The TypeScript SDK can also attach **local JS function stages** and debug/logging stages directly in-process, without the registry — useful for local development and framework integration. See [TypeScript SDK → Stages](/sdks/typescript#local-stages). CEL stages are the portable, registry-delivered kind that tests use across all languages.
