actions — Commands + Rules

Intents define commands and the rules that execute when they are submitted. In the DSL they are declared under the actions: key. Rules execute in three phases: preflightcoreside_effects.

actions:
  CHECK_IN_TO_SHOW:
    state: user
    entity_id_expr: intent.user_id    # optional; defaults to entity_key
    description: "Record a concert check-in"
    input:
      user_id:  { type: string, required: true }
      show_id:  { type: string, required: true }
      venue_id: { type: string, required: true }
 
    preflight:
      rules:
        - name: reject_duplicate_checkin
          when:
            expr: "contains(entity.checkins, event.show_id)"
          then:
            - op: reject
              code: ALREADY_CHECKED_IN
              message: "Already checked in to this show"
 
    core:
      rules:
        - name: record_checkin
          when: {}
          then:
            - op: push
              path: /checkins
              value: event.show_id
            - op: add
              path: /shows_attended
              value: 1
            - op: emit
              event_type: SHOW_CHECK_IN_CREATED
              payload:
                user_id:  event.user_id
                show_id:  event.show_id
                venue_id: event.venue_id
 
    side_effects:
      rules:
        - name: notify_friends
          when: {}
          then:
            - op: submit
              intent_type: DELIVER_CHECKIN_NOTIFICATION
              payload:
                user_id:  event.user_id
                show_id:  event.show_id

Phases

PhasePurposeAllowed ops
preflightValidation; abort with rejectreject, read-only checks, LOOKUP_FIELD
corePrimary state mutations + emit eventsAll mutation ops, emit, emit_each, relationship_create, relationship_remove
side_effectsExternal I/O after core commits; failures do not roll back coreemit, emit_each, submit, schedule, decision

Rule structure

- name: my_rule_name        # snake_case; required
  priority: 100             # lower = runs first; default 100
  when:
    expr: "entity.count > 0"   # optional condition
  then:
    - op: ...

when: {} (empty object) means the rule always runs when the action is triggered.

⚠️

when.expr vs filter.where — different shapes

ClauseExpected shape
Rule when: expr:Object: when: { expr: "condition" }
filter / remove / find where:Plain string: where: "it.qty > 0"

Using where: { expr: "..." } under filter causes ClassCastException: LinkedHashMap cannot be cast to String at compile time.


Next steps