← Developers

02.2 — Engine 2: Rules (formula, constraint, data-link)

Spec matching SPEC-DX-01 / rules v1

The Invariant

Every rule evaluated by the platform is a pure function of (payload, context, db state). The expression language is deterministic by construction — there are no function calls in the grammar, so a manifest cannot express now(), random() or uuid(). The same event replayed on the same state produces the same verdict, forever.

The Contract

Three engines share the same expression evaluator:

Scope prefixes: $payload (caller input), $context (session: operatorId, tenant), $db (lookup), $formula (computed values). Anything outside these scopes is rejected at manifest load.

The Flow

  1. The action engine binds $payload from the request and $context from the session.
  2. Formulas referenced by the trigger’s constraints are evaluated first.
  3. Each pre_commit_rule for the trigger is evaluated; any false → CONSTRAINT_VIOLATED, rollback.
  4. On success the trigger’s data-link query runs — SQL is generated from the manifest mapping, never interpolated from raw input.
  5. Results land in the same commit as the FSM transition and audit row.

Payload Example

// fragment of manifests/LayoutManifest.production-line.example.json
{
  "formula_engine": {
    "weight_delta_pct": "(($payload.peso_misurato - $payload.peso_dichiarato) / $payload.peso_dichiarato) * 100"
  },
  "constraint_engine": {
    "pre_commit_rules": [{
      "id": "peso_coerente",
      "expression": "$formula.weight_delta_pct <= 0.5 && $formula.weight_delta_pct >= -0.5",
      "error_message": "Peso misurato fuori tolleranza rispetto al dichiarato",
      "triggers": ["ACCEPT_LOT"]
    }]
  },
  "data_link_engine": {
    "queries": {
      "ACCEPT_LOT": {
        "type": "INSERT", "table": "biz_lotti",
        "mapping": { "lotto": "$payload.lot_id",
                     "peso_misurato": "$payload.peso_misurato",
                     "operator_id": "$context.operatorId" }
      }
    }
  }
}

Known Failures

Symptom Cause
Manifest rejected at load Expression outside the grammar (function call, unknown scope) — rewrite against $payload/$context/$db/$formula
CONSTRAINT_VIOLATED on valid-looking data A formula upstream produced an out-of-envelope value — evaluate it on the same payload
Non-biz_ table error Data-link writes are restricted to the biz_ schema surface by the SQL guard
Rule fires on the wrong trigger triggers: [...] list too broad — constraints are opt-in per trigger