Rule Cascade
ReferenceDecision records (ADRs)

ADR 0001: Expressions are a JSON tree of {op, args} nodes

Status: accepted

Status: accepted

Context

Rules need conditions that a browser, a JVM service and other runtimes evaluate identically. The candidates were JSON Logic, CEL, a vendor format (GoRules JDM) and our own small tree.

Decision

Conditions are a JSON tree: a literal, { "var": "data.amount" }, an operator call { "op": "lte", "args": [ ... ] } or a function call { "fn": "isBlank", "args": [ ... ] }, with a fixed operator list per profile and strict typing. Function calls and host-supplied x- operators were added later (ADR 0008); the shape did not change.

Why

  • JSON Logic has the right idea and many ports, but its implementations disagree on truthiness and edge cases; a community effort is under way to tighten the specification. Its shape, {"<=": [a, b]}, puts the operator in the key, which plain JSON Schema cannot enumerate cleanly and OpenAI strict structured outputs cannot express at all.
  • CEL is typed, non-Turing-complete and has a conformance suite. It is a text language: a rule builder UI must parse and print it, and the TypeScript implementation was still marked beta when this was decided.
  • A vendor format ties the contract to one company's roadmap.
  • {op, args} makes the operator a value. It validates with an enum, maps to sealed types in Java and discriminated unions in TypeScript, is trivially walkable for static checks, and a model can be constrained to produce it.

Consequences

  • We own the semantics, so we own a conformance suite. That is the price, and it is paid in conformance/.
  • JSON Logic remains reachable. rulecheck jsonlogic import|export converts in both directions. The conversion is not purely mechanical, because JSON Logic converts types and Rule Cascade does not: the converter marks each construct as equivalent, as converted with a stated caveat, or as refused. The comparison and the full mapping are in JSON Logic and Rule Cascade.
  • Authors write more characters than in CEL. A text front end that compiles to the tree can be added later without changing the contract.

On this page