Docs
Rule DSL reference
Grammar, condition operators, built-ins, actions, priority bands, conflict order, and complete WB-1 rule examples.
Rules are versioned data evaluated once per 1 kHz engine cycle. A candidate revision is parsed and validated before activation; an unknown key fails the revision and leaves the active set untouched.
Grammar
rule := "rule:" id newline priority when then
priority := "priority:" integer[1..100]
when := "when:" ("all:" condition+ | "any:" condition+)
condition := input comparator value | builtin comparator value
then := "then:" action+
action := output set value | emit event | hold_until condition
Canonical YAML shape:
rule: descriptive-kebab-id
priority: 50
when:
all:
- input: domain.zone.signal
gte: 1
then:
- output: domain.zone.actuator
set: value
- emit: audit.descriptive_event
Condition operators
| Operator | Value types | Meaning |
|---|---|---|
equals, not_equals | scalar, enum, boolean | exact normalized value |
gt, gte, lt, lte | number, duration | ordered comparison |
between | number, time | inclusive two-value range |
in, not_in | scalar + list | membership |
changed | channel path | value changed this cycle |
stale_for | channel path + duration | no accepted sample within duration |
matches | string + bounded regex | RE2 expression, 128-character limit |
Units are normalized before comparison. Comparing 4 mA with a channel declared
in volts fails validation instead of guessing a conversion.
Built-ins
| Built-in | Returns | Example |
|---|---|---|
time_of_day() | local time | between: ["06:00", "09:00"] |
zone() | current zone ID | equals: court.3 |
rolling_avg(path, window) | number | rolling_avg(vibration.rms, 5s) |
rate(path, window) | number / second | rate(door.edge, 30s) |
age(path) | duration | age(sensor.temp) > 2s |
ml.* | model output | ml.anomaly_score > 0.8 |
ml.* values are ordinary normalized inputs. They do not bypass priorities or
safety rules.
Action paths
Outputs use stable domain.zone.id paths. A single segment may be *; a
bounded range uses 4..8.
- output: light.court.*.scorebar
set: freeze
- output: audio.court.4..8.horn
pulse: 800ms
- output: hvac.zone.2.setpoint
set: 68F
- emit: audit.period_end
Wildcards resolve to the current device map at activation time and are written to the audit record as concrete paths.
Priority bands
| Band | Convention | Examples |
|---|---|---|
| 90–100 | safety and hard interlocks | E-stop hold, egress lighting, over-temp cutoff |
| 50–89 | orchestration and operator intent | game cues, room plans, changeover recipes |
| 1–49 | ambient and convenience | soft-open lighting, comfort setpoints |
Conflicts resolve by priority, then freshest input, then the rule with more matched conditions. The audit entry records both contenders and the deciding key. See the engine for the timing path.
Example — sports period end
rule: tournament-period-end
priority: 86
when:
all:
- zone: court.*
- event: scoreboard.period_end
- confidence_gte: 0.98
then:
- light.court.*.scorebar: freeze
- audio.court.*.horn: pulse_800ms
- emit: audit.tournament_period_end
Example — gym soft-open
rule: gym-entrance-soft-open
priority: 40
when:
all:
- input: sensor.door.reed
equals: closed
- time_of_day:
between: ["06:00", "09:00"]
then:
- output: light.zone.2
fade_to: {value: 30%, over: 5s}
- output: hvac.zone.2.setpoint
set: 68F
Example — industrial E-stop hold
rule: cell4-estop-hold
priority: 100
when:
any:
- input: cell.4.estop_a
equals: open
- input: cell.4.estop_b
equals: open
then:
- output: cell.4.drive_enable
set: false
- output: cell.4.air_dump
set: true
- hold_until: safety.cell4.manual_reset
- emit: audit.safety_hold
A priority-100 rule can still be replaced only by a reviewed revision; it cannot
be suppressed by an ambient rule. Validate with POST /api/v1/rules:validate
before fleet activation.
Audit fields
Every firing and conflict record includes event_id, rule_id, revision,
matched_inputs, resolved_outputs, priority, deciding_key, device_id,
zone, cycle, ts, previous_hash, and hash. Export is hash-chained NDJSON.
The API overview documents paging and
verification.