Rule Cascade
LearnValues and types

Values, null and no coercion

The string "1" is not the number 1, a missing path is null, equality is deep, and list indexes are path segments.

Values are JSON values, and the engine never converts one type into another. The number 1 is not the string "1", and true is not 1. A path that does not exist evaluates to null. In a path, a segment of digits is a list index: data.items.0.sku is the SKU of the first line.

Syntax

values
{ op: eq, args: [{ var: data.priority }, 1] }     # true only for the number 1
{ op: eq, args: [{ var: data.priority }, "1"] }   # true only for the string "1"
{ var: data.items.0.sku }                          # first element; null if absent
RuleMeaning
No coercion1 is not "1"; true is not 1
Missing is nullA path that does not resolve is null, never an error
Deep equalityeq compares lists element by element and objects key by key; 1 equals 1.0
IndexesA digits-only segment indexes a list; past the end is null
Type errorslt on a string, a wrong argument count, an unknown operator: an evaluation error

Example

An express order needs priority 1. The request sends the string "1", so it is denied.

no-coercion.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.no-coercion, version: 1.0.0, title: "No coercion" }
scope:
  - { level: organization, id: learn }
entities:
  Order:
    schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
  - id: order.express.priority
    kind: validation
    target: { entity: Order, field: /priority }
    operations: [create]
    when: { op: eq, args: [{ var: data.express }, true] }
    assert: { op: eq, args: [{ var: data.priority }, 1] }
    severity: error
    finding: { code: LRN-VAL-001, message: order.expressPriority }
  - id: order.first-item.sku
    kind: validation
    target: { entity: Order, field: /items }
    operations: [create]
    when: { op: exists, args: [{ var: data.items }] }
    assert: { op: exists, args: [{ var: data.items.0.sku }] }
    severity: error
    finding: { code: LRN-VAL-002, message: order.firstItemSku }
messages:
  en:
    order.expressPriority: "Express orders have priority 1."
    order.firstItemSku: "The first line needs a SKU."
tests:
  - name: the string "1" is not the number 1
    entity: Order
    operation: create
    given:
      data: { express: true, priority: "1" }
    expect:
      decision: deny
      findings:
        - { rule: order.express.priority, fields: [/priority] }
  - name: the number 1 passes
    entity: Order
    operation: create
    given:
      data: { express: true, priority: 1 }
    expect: { decision: allow, findings: [] }
  - name: a missing SKU is null, so exists is false
    entity: Order
    operation: create
    given:
      data: { items: [{ qty: 1 }] }
    expect:
      decision: deny
      findings:
        - { rule: order.first-item.sku, fields: [/items] }
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "express": true,
    "priority": "1"
  }
}

Result, from the engine

Decisiondeny1 finding, server channel

  • LRN-VAL-001errorblockingExpress orders have priority 1./priority
Try it YourselfOpens this ruleset and request in the playground. Nothing to install.

Common mistakes

  • Sending numbers as strings from a form. Convert form input before you evaluate, or accept both types on purpose.
  • Unquoted YAML scalars. YAML 1.1 parsers read no, on, yes, 012 and 2026-10-03 as other types than YAML 1.2 parsers do. Quote them: "no", "2026-10-03". The checker reports an unquoted one as YAML_NOT_PORTABLE. Do not use anchors, aliases or merge keys either.
  • Treating null as an error. exists and empty test for it; coalesce gives a default.

Exercise

Make the priority rule accept both the number 1 and the string "1", and nothing else. Change the first golden test so it expects allow.

Hint

Wrap two eq calls in an or: one compares with the number 1, the other with the string "1".

Show answer
no-coercion.ruleset.yaml
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.no-coercion, version: 1.0.0, title: "No coercion" }
scope:
  - { level: organization, id: learn }
entities:
  Order:
    schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
rules:
  - id: order.express.priority
    kind: validation
    target: { entity: Order, field: /priority }
    operations: [create]
    when: { op: eq, args: [{ var: data.express }, true] }
    assert: { op: or, args: [{ op: eq, args: [{ var: data.priority }, 1] }, { op: eq, args: [{ var: data.priority }, "1"] }] }
    severity: error
    finding: { code: LRN-VAL-001, message: order.expressPriority }
  - id: order.first-item.sku
    kind: validation
    target: { entity: Order, field: /items }
    operations: [create]
    when: { op: exists, args: [{ var: data.items }] }
    assert: { op: exists, args: [{ var: data.items.0.sku }] }
    severity: error
    finding: { code: LRN-VAL-002, message: order.firstItemSku }
messages:
  en:
    order.expressPriority: "Express orders have priority 1."
    order.firstItemSku: "The first line needs a SKU."
tests:
  - name: the string "1" is accepted now
    entity: Order
    operation: create
    given:
      data: { express: true, priority: "1" }
    expect: { decision: allow, findings: [] }
  - name: priority 2 is still denied
    entity: Order
    operation: create
    given:
      data: { express: true, priority: 2 }
    expect:
      decision: deny
      findings:
        - { rule: order.express.priority, fields: [/priority] }
  - name: the number 1 passes
    entity: Order
    operation: create
    given:
      data: { express: true, priority: 1 }
    expect: { decision: allow, findings: [] }
  - name: a missing SKU is null, so exists is false
    entity: Order
    operation: create
    given:
      data: { items: [{ qty: 1 }] }
    expect:
      decision: deny
      findings:
        - { rule: order.first-item.sku, fields: [/items] }
request.json
{
  "entity": "Order",
  "operation": "create",
  "data": {
    "express": true,
    "priority": "1"
  }
}

Result, from the engine

Decisionallow0 findings, server channel

Course overview

On this page