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:
"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:
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.
# 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
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
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:
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)
Keep nesting shallow. If you can’t describe the rule in one sentence, consider splitting it into two named conditions.
Multiple conditions
You can define multiple top-level conditions. The run stops when any one of them fires:
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:
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:
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