StateEntity State

Entity State

Entity state is defined in the state: section of the DSL. Each entity type has a named entry with an entity_key and a list of typed fields. State is stored in the entity_snapshots table and is the input to all rule evaluation.

State Definition Syntax

state:
  <entity_type>:
    entity_key: <field_name>
    fields:
      - name: <field_name>
        type: <type>
        default: <value>

Supported Types

TypeDescriptionExample default
stringUTF-8 string""
intInteger0
numberFloating-point0
booleanTrue/falsefalse
datetimeISO 8601 timestamp""
arrayTyped array (requires item_type)[]
objectNested object{}

Array Fields

Array fields require item_type. If the array contains objects, define item_fields:

- name: top_artists
  type: array
  item_type: object
  item_fields:
    artist_id: { type: string, required: true }
    play_count: { type: int, required: true }
  default: []

Warning: Always declare a default for array fields. Arrays with a null default break contains() and size() expressions, causing runtime errors in rules.

Object Fields

- name: preferences
  type: object
  default: {}

Nested fields within objects are accessed via dot notation in expressions and slash notation in op paths.

Full Entity State Example

state:
  user:
    entity_key: user_id
    fields:
      - name: concert_count
        type: int
        default: 0
      - name: favorite_venue
        type: string
        default: ""
      - name: total_distance_traveled
        type: number
        default: 0
      - name: top_artists
        type: array
        item_type: object
        item_fields:
          artist_id: { type: string, required: true }
          play_count: { type: int, required: true }
        default: []
      - name: last_concert_date
        type: datetime
        default: ""
      - name: preferences
        type: object
        default: {}

Path Notation

Causet uses two notations depending on context:

Slash Notation (ops and when paths)

Used in op targets and when condition paths:

then:
  - op: set
    path: /favorite_venue
    value: "The Stone Pony"
 
when:
  - path: /concert_count
    op: gte
    value: 10

For nested fields, use slash-separated paths:

- op: set
  path: /inventory/gold
  value: 100

Dot Notation (expressions)

Used in value: expressions, derive: expressions, and intent payload references:

then:
  - op: push
    path: /top_artists
    value:
      artist_id: intent.artist_id
      play_count: 1

Accessing nested entity state in expressions:

derive:
  venue_name: entity.preferences.default_venue

Scratch Space: _tmp/ Prefix

Fields prefixed with _tmp/ are scratch space. They are available in all rule contexts but are intended for transient state — workflow steps, saga tracking, intermediate computations.

state:
  ticket_import:
    entity_key: import_id
    fields:
      - name: _tmp/saga_step
        type: int
        default: 0
      - name: raw_data
        type: string
        default: ""
      - name: venue_name
        type: string
        default: ""

_tmp/saga_step is writable by ordinary core rules (op: set) and readable by preflight rules — it lives in the entity snapshot alongside domain fields. A sagas: block writes to the same kind of _tmp/ field automatically (via a compiler-generated core rule per step, see Defining Sagas), but you can also set it yourself directly, as in Your First Workflow.

In slash notation: /_tmp/saga_step In dot notation: entity._tmp.saga_step

Entity State in Rule Contexts

Entity state is available in all rule evaluation contexts as entity.*:

preflight:
  rules:
    - name: sufficient_balance
      when:
        - path: /balance
          op: gte
          value: intent.amount
      error:
        code: INSUFFICIENT_BALANCE
        message: "balance too low"
 
core:
  rules:
    - name: deduct_balance
      when: {}
      then:
        - op: add
          path: /balance
          value: "-intent.amount"

Intent payload values are available as intent.*. Entity state is available as entity.*. Both are resolved at evaluation time against the current entity snapshot.

State Persistence

Entity state is persisted to PostgreSQL by the causet-projection-worker. The entity_snapshots table contains one row per entity, with the full state as a JSON column.

SELECT entity_id, state
FROM entity_snapshots
WHERE entity_type = 'user'
  AND entity_id = 'user_abc123';

The state column contains the full entity state object, including all _tmp/ fields:

{
  "concert_count": 12,
  "favorite_venue": "The Stone Pony",
  "total_distance_traveled": 847.5,
  "top_artists": [
    { "artist_id": "artist_pearl_jam", "play_count": 4 },
    { "artist_id": "artist_springsteen", "play_count": 3 }
  ],
  "_tmp": {
    "saga_step": 0
  }
}

State is Rebuilt from Ledger

Entity snapshots are derived artifacts. The source of truth is the ledger_events table. If you drop the entity_snapshots table and replay the ledger through the rules engine, you get the same snapshots.

This means:

  • Snapshots can be rebuilt after schema changes
  • The ledger is the audit log for all state transitions
  • New fields added to the state definition can be backfilled by rebuilding from the ledger