PR P: Pluggable Rules Engine (JSON DSL) #20

Merged
nsatoshi merged 1 commits from devin/1776882394-pr-p-rules-engine into main 2026-04-22 20:30:23 +00:00
Owner

Closes gap-analysis v2 §5.2 partial — the arch §5.2 Rules Engine was marked "partial (hardcoded rules)".

What

New: services/rulesEngine.ts — declarative JSON DSL for business-rule evaluation.

Operators

eq · neq · gt · gte · lt · lte · in · not_in · exists · matches (regex) · length_gte · length_lte

Combinators

{all: [...]} · {any: [...]} · {not: ...}

Rule shape

{
  "id": "plan.pay_step_present",
  "when": { "path": "compliance", "op": "exists" },
  "assert": { "path": "plan.steps[0].type", "op": "eq", "value": "pay" },
  "severity": "error"
}
  • when (optional) — gates the rule; when false, the rule is skipped.
  • assert — condition the rule requires to hold.
  • severity: "warn" reports but does not block (ok: true).

Safety

  • No eval. Evaluator is a closed recursive switch over the operator set.
  • Pure. No side effects, deterministic, replayable.
  • Silent on bad input — unknown operators return false, bad regex returns false.

Built-ins

  • preconditions.builtin — arch §8 PRECONDITIONS_PENDING → READY_FOR_PREPARE (plan exists, ≥1 step, pay step present, ≥1 participant, KYC=ok when compliance present).
  • commit.builtin — arch §9.2 (DLT 0x+64-hex tx hash, bank ISO message id present, state=VALIDATING, exceptions.active empty).

Pluggability

RULES_FILE=/path/to/rules.json — JSON map {ruleSetId: RuleSet} overriding built-ins by id. Silent fall-through to built-ins on load failure (matches the existing services/hsm.ts dev-friendly pattern).

Verification

  • npx tsc --noEmit clean.
  • npx jest 96/96 green.
  • 16 new unit tests: operators, combinators, when gating, severity semantics, built-in rule sets, loader behaviour, empty-ruleset fallback for unknown ids.

Follow-ups (intentionally out-of-scope)

  • Migrate call sites (ExecutionCoordinator, transitions) to the engine — doing it here would balloon the diff. Built-ins mirror the current hardcoded checks exactly, so the migration is a drop-in.
Closes gap-analysis v2 §5.2 partial — the arch §5.2 Rules Engine was marked "partial (hardcoded rules)". ## What **New**: `services/rulesEngine.ts` — declarative JSON DSL for business-rule evaluation. ### Operators `eq` · `neq` · `gt` · `gte` · `lt` · `lte` · `in` · `not_in` · `exists` · `matches` (regex) · `length_gte` · `length_lte` ### Combinators `{all: [...]}` · `{any: [...]}` · `{not: ...}` ### Rule shape ```json { "id": "plan.pay_step_present", "when": { "path": "compliance", "op": "exists" }, "assert": { "path": "plan.steps[0].type", "op": "eq", "value": "pay" }, "severity": "error" } ``` - `when` (optional) — gates the rule; when false, the rule is skipped. - `assert` — condition the rule requires to hold. - `severity: "warn"` reports but does not block (`ok: true`). ### Safety - **No eval**. Evaluator is a closed recursive switch over the operator set. - **Pure**. No side effects, deterministic, replayable. - **Silent on bad input** — unknown operators return `false`, bad regex returns `false`. ## Built-ins - `preconditions.builtin` — arch §8 PRECONDITIONS_PENDING → READY_FOR_PREPARE (plan exists, ≥1 step, pay step present, ≥1 participant, KYC=ok when compliance present). - `commit.builtin` — arch §9.2 (DLT 0x+64-hex tx hash, bank ISO message id present, state=VALIDATING, exceptions.active empty). ## Pluggability `RULES_FILE=/path/to/rules.json` — JSON map `{ruleSetId: RuleSet}` overriding built-ins by id. Silent fall-through to built-ins on load failure (matches the existing `services/hsm.ts` dev-friendly pattern). ## Verification - `npx tsc --noEmit` clean. - `npx jest` 96/96 green. - 16 new unit tests: operators, combinators, `when` gating, severity semantics, built-in rule sets, loader behaviour, empty-ruleset fallback for unknown ids. ## Follow-ups (intentionally out-of-scope) - Migrate call sites (ExecutionCoordinator, transitions) to the engine — doing it here would balloon the diff. Built-ins mirror the current hardcoded checks exactly, so the migration is a drop-in.
nsatoshi added 1 commit 2026-04-22 18:28:51 +00:00
Pluggable Rules Engine with JSON DSL
Some checks failed
CI / Frontend Lint (pull_request) Failing after 7s
CI / Frontend Type Check (pull_request) Failing after 6s
CI / Frontend Build (pull_request) Failing after 7s
CI / Frontend E2E Tests (pull_request) Failing after 7s
CI / Orchestrator Build (pull_request) Failing after 7s
CI / Contracts Compile (pull_request) Failing after 6s
CI / Contracts Test (pull_request) Failing after 6s
Code Quality / SonarQube Analysis (pull_request) Failing after 23s
Code Quality / Code Quality Checks (pull_request) Failing after 4s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 3s
72ff0e4cc0
Closes gap-analysis v2 §5.2 partial (Rules Engine was hardcoded).

- services/rulesEngine.ts — declarative JSON DSL with a closed
  operator set (eq/neq/gt/gte/lt/lte/in/not_in/exists/matches/
  length_gte/length_lte) + AND/OR/NOT combinators. No eval, no
  runtime code injection. Dotted + indexed path resolver.
- evaluate(ruleSet, context) returns {ok, failures}; 'error'
  severity blocks, 'warn' is reported but non-blocking. 'when'
  clauses gate a rule (e.g. only check compliance.kyc if the
  compliance block is present at all).
- Built-in rule sets mirror the pre-DSL hardcoded checks:
    preconditions.builtin — plan + pay step + participants + KYC
    commit.builtin        — dlt tx hash + bank iso msg id +
                            state=VALIDATING + no exceptions (arch §9.2)
- Pluggable: RULES_FILE env points at a JSON map overriding any
  built-in by id. Silent fall-through to built-ins on error.
- 16 unit tests across operators, combinators, severity semantics,
  'when' gating, built-in rule sets, and loader behaviour.
- Full suite 96/96 green; tsc --noEmit clean.
nsatoshi merged commit 351bb472b6 into main 2026-04-22 20:30:23 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: d-bis/CurrenciCombo#20