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.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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_lteCombinators
{all: [...]}·{any: [...]}·{not: ...}Rule shape
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
false, bad regex returnsfalse.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 existingservices/hsm.tsdev-friendly pattern).Verification
npx tsc --noEmitclean.npx jest96/96 green.whengating, severity semantics, built-in rule sets, loader behaviour, empty-ruleset fallback for unknown ids.Follow-ups (intentionally out-of-scope)
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.