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

# Early Termination

> Stop simulation runs automatically when a condition is met, without waiting for the job timeout.

Early termination lets you define stopping rules that the runner evaluates against live telemetry during execution. When a rule fires, the runner kills the simulation process immediately and marks the job failed — no need to wait for `job_timeout_seconds`.

This is useful for catching degenerate runs early: an orbit that has decayed, a vehicle that is tumbling, a battery that has hit a critical floor. Stopping bad runs fast saves compute and keeps your results clean.

## Enabling early termination

Early termination is configured in the `early_termination` block of your batch submission:

```json theme={null}
"early_termination": {
  "enabled": true,
  "check_frequency_hz": 10.0,
  "conditions": [...]
}
```

| Field                | Type    | Description                                                                          |
| -------------------- | ------- | ------------------------------------------------------------------------------------ |
| `enabled`            | boolean | Whether early termination is active for this batch                                   |
| `check_frequency_hz` | number  | How many times per second the runner evaluates conditions against incoming telemetry |
| `conditions`         | array   | One or more named stopping rules                                                     |

Set `enabled: false` to include conditions in your config without activating them — useful for staging rules before you trust them in production.

## Simple condition

Each condition compares one telemetry variable against a threshold:

```yaml theme={null}
conditions:
  - name: battery_low
    description: Battery critically low
    variable: battery_state_of_charge
    operator: lt
    threshold: 10.0
    units: percent
```

This reads as: **stop the run if `battery_state_of_charge < 10.0 percent`**.

### Condition fields

| Field         | Required    | Description                                                                   |
| ------------- | ----------- | ----------------------------------------------------------------------------- |
| `name`        | yes         | Identifier for this rule. Appears in logs and trigger events.                 |
| `description` | recommended | Human-readable explanation of why this rule exists                            |
| `variable`    | yes         | The telemetry variable name to evaluate — must match what your scenario emits |
| `operator`    | yes         | Comparison operator (see below)                                               |
| `threshold`   | yes         | The value to compare against                                                  |
| `units`       | yes         | Units of the threshold value — must match what your scenario reports          |

### Operators

| Operator | Meaning               |
| -------- | --------------------- |
| `lt`     | less than             |
| `lte`    | less than or equal    |
| `gt`     | greater than          |
| `gte`    | greater than or equal |
| `eq`     | equal                 |
| `neq`    | not equal             |

### Units

`units` is explicit metadata — Lynx does not infer unit conversions. The value must match the scale your scenario uses when emitting that variable.

```yaml theme={null}
# If your scenario emits battery as 0–100:
threshold: 10.0
units: percent

# If your scenario emits battery as 0.0–1.0:
threshold: 0.10
units: fraction
```

Common unit strings: `percent`, `fraction`, `km`, `m`, `deg`, `deg/s`, `rad/s`, `boolean`, `dimensionless`.

## Grouped conditions

For multi-variable logic, use `all` (AND) or `any` (OR) instead of a single `variable` field.

### `all` — every condition must be true

```yaml theme={null}
conditions:
  - name: unsafe_orbit
    description: Low battery during orbital decay
    all:
      - variable: battery_state_of_charge
        operator: lt
        threshold: 10.0
        units: percent
      - variable: altitude_km
        operator: lt
        threshold: 150.0
        units: km
```

Fires when: `battery_state_of_charge < 10.0%` **AND** `altitude_km < 150.0 km`

### `any` — at least one condition must be true

```yaml theme={null}
conditions:
  - name: vehicle_unstable
    description: Vehicle is tumbling or battery is critically low
    any:
      - variable: body_spin_rate_deg_s
        operator: gt
        threshold: 15.0
        units: deg/s
      - variable: battery_state_of_charge
        operator: lt
        threshold: 10.0
        units: percent
```

Fires when: `body_spin_rate_deg_s > 15.0 deg/s` **OR** `battery_state_of_charge < 10.0%`

## Nested groups

`all` and `any` can be nested when the logic requires it:

```yaml theme={null}
conditions:
  - name: landing_abort
    description: Abort if low altitude and either unstable or poorly localized
    all:
      - variable: altitude_m
        operator: lt
        threshold: 30.0
        units: m
      - any:
          - variable: body_spin_rate_deg_s
            operator: gt
            threshold: 15.0
            units: deg/s
          - variable: vio_tracking_quality
            operator: lt
            threshold: 0.3
            units: dimensionless
```

Fires when: `altitude_m < 30.0 m` **AND** (`body_spin_rate_deg_s > 15.0 deg/s` **OR** `vio_tracking_quality < 0.3`)

<Tip>
  Keep nesting shallow. If you can't describe the rule in one sentence, consider splitting it into two named conditions.
</Tip>

## Multiple conditions

You can define multiple top-level conditions. The run stops when **any one of them** fires:

```yaml theme={null}
conditions:
  - name: battery_low
    description: Battery critically low
    variable: battery_state_of_charge
    operator: lt
    threshold: 10.0
    units: percent

  - name: orbital_decay
    description: Orbit has decayed below safe altitude
    variable: altitude_km
    operator: lt
    threshold: 150.0
    units: km
```

## Trigger events

When a condition fires, the runner records a structured event that appears in your job result and platform logs:

| Field                  | Description                                      |
| ---------------------- | ------------------------------------------------ |
| `rule_name`            | The `name` you gave the condition                |
| `description`          | The `description` from your config               |
| `elapsed_seconds`      | How far into the run the rule fired              |
| `telemetry_at_trigger` | The telemetry frame that caused the rule to fire |

This makes every early termination explainable — you can see exactly which condition fired, when, and what the simulation state was at that moment.

## Schema reference

**Simple condition:**

```yaml theme={null}
early_termination:
  enabled: true
  check_frequency_hz: 10.0
  conditions:
    - name: string
      description: string
      variable: string
      operator: lt | lte | gt | gte | eq | neq
      threshold: number
      units: string
```

**Grouped condition:**

```yaml theme={null}
early_termination:
  enabled: true
  check_frequency_hz: 10.0
  conditions:
    - name: string
      description: string
      all:               # or any:
        - variable: string
          operator: lt | lte | gt | gte | eq | neq
          threshold: number
          units: string
        - variable: string
          operator: lt | lte | gt | gte | eq | neq
          threshold: number
          units: string
```
