# Policy Inputs and Expressions

Each rule evaluates a boolean CEL expression or Starlark function body against a captured input object. Predicates do not
execute commands, make network requests, read files, or collect additional inputs.

## Expression variables

The table below uses CEL syntax. [Starlark policies](/policies/starlark) receive the same snapshots
as `ctx.input`, `ctx.params`, `ctx.ci`, and `ctx.request`, with dictionary indexing for nested fields.

| Variable | Type | Contents |
|---|---|---|
| `input` | Object | Data for the rule's `on` kind |
| `params` | Object | Rule's `with` values; empty object by default |
| `ci` | Object or `null` | [Provider-native CI snapshot](/policies/ci); `null` when no provider is detected |
| `request.kind` | String | `terraform.plan`, `operation`, `atmos.affected`, `function`, `source`, `command`, or `component.command` |
| `request.revisions` | Object | `governing`, `base`, `head`; empty strings when not supplied |
| `request.subject` | Object or `null` | Instance identity and metadata for plan/lifecycle attachments |

`ci` is a snapshot object, not the boolean `ci` variable used by ordinary hook/workflow conditions.
Affected analysis populates comparison `base` and `head` commit IDs. The `governing` revision is not
inferred from either comparison revision. Plan and lifecycle integrations leave revision fields empty.

To discover fields, open **Examples** on a [library policy](/policies/library), choose a scenario,
and select **Field explorer**. It shows types and copyable CEL/Starlark expressions for each tested input. For the actual
CI payload in your job, run [`atmos ci context --paths`](/cli/commands/ci/context).

## Admission inputs

See [Execution Admission](/policies/admission) for function names, CLI command paths, and
component command capabilities. These inputs are available without affected analysis.

For `on: source`, see [Source and Filesystem Policies](/policies/sources) for destination fields
and local path confinement. Source rules run before guarded reads and downloads.

## Operation input

Use `on: operation` for Terraform, Helmfile, Helm, and Kubernetes lifecycle events.

```json
{
  "event": "after.terraform.plan",
  "component_type": "terraform",
  "command": "plan",
  "component": "vpc",
  "stack": "prod-us-east-1",
  "status": "success",
  "exit_code": 2,
  "dry_run": false
}
```

| Field | Type | Meaning |
|---|---|---|
| `event` | String | Canonical `before.<component_type>.<command>` or `after.<component_type>.<command>` lifecycle event |
| `component_type` | String | `terraform`, `helmfile`, `helm`, or `kubernetes` |
| `command` | String | Operation represented by the normalized event; deploy aliases use apply events |
| `component` | String | Component argument |
| `stack` | String | Selected stack |
| `status` | String | `success` or `failure`; before-events begin with `success`. A Terraform plan exit code of `2` counts as success unless policy enforcement failed |
| `exit_code` | Integer | Recorded command exit code; not proof that policies passed |
| `dry_run` | Boolean | Whether this invocation is a dry-run |

Select the lifecycle event explicitly:

```cel
input.event == 'before.helm.delete' && input.stack.startsWith('prod-')
```

See [Hooks](/policies/hooks) for event selection and failure behavior. Operation input contains
execution metadata, not resolved component settings or a changed-files/affected-component inventory.

## Terraform plan input

With `on: terraform.plan`, `input` is the native saved-plan JSON object. See
[Terraform Plans](/policies/terraform-plan#terraform-plan-input) for extraction and validation rules.

```cel
has(input.resource_changes) && input.resource_changes.exists(r,
  r.mode == 'managed' && 'delete' in r.change.actions)
```

## Affected input

Use `on: atmos.affected` to inspect changed files, instances, components, stacks, and structured
fields before affected results are consumed. See [Affected Changes](/policies/affected) for the
input contract, examples, collection scope, and enforcement requirements.

## Missing and unknown values

| Condition | Handling |
|---|---|
| Optional field absent | Use `has(object.field)` before accessing it |
| Field present with `null` | Check `object.field != null` before reading nested values |
| Terraform value unknown until apply | Inspect `change.after_unknown`; missing planned data is not evidence of compliance |
| CI data required for a decision | Declare `required_ci`; do not substitute an empty object |
| Expression error or non-boolean result | Block the command |

Use presence checks only when absence is acceptable. For a required attribute, deny when it is
missing, null, empty, or unknown. See the [required Owner tag example](/policies/examples).

## Evaluation results

| Result | Effect |
|---|---|
| Predicate `false` | This rule does not apply |
| Predicate `true`, `action: deny` | Block; another rule cannot override the denial |
| Predicate `true`, `action: hooks` | Require the selected hooks according to their execution/success flags |
| Wrong input kind | Rule does not apply to this request |
| Missing required input, invalid expression, or evaluation error | Block |

CEL evaluation has a runtime cost limit of 100,000. Expressions over large inputs may hit this
limit and block rather than evaluate without a bound.
