Getting Started

What's New in 2.0.0

Released July 2026

New

RuleFlow — Pipeline Orchestration

The headline feature of 2.0.0: a fluent, pipeline-style API for composing Rules, RuleSets, and other RuleFlows into a single executable flow — with typed input parameters, conditionals, loops, scopes, exception handling, and async execution built in. RuleFlow is now the recommended default way to run rules and rulesets, replacing hand-rolled orchestration around direct run() calls.

RuleFlow<Boolean> flow = RuleFlow.builder()
    .name("myFlow")
    .bind(x -> 42)
    .run(rule, spec -> spec.as("result")
        .onException(UnrulyException.class, b -> b.bind(ok -> false)))
    .when(cond, b -> b.run(rule2))
    .onException(Exception.class, b -> b.bind(handled -> true))
    .<Boolean>returning(function((Boolean handled) -> handled))
    .build();
RuleFlows →
New

Async Pipeline & AsyncRunnable

RuleFlows launch steps asynchronously with asyncRun(...) and coordinate them with await / awaitAll / awaitAny, non-blocking thenRun continuations, and async exception handlers. A new AsyncRunnable interface adds runAsync(RuleContext) to RuleSet and RuleFlow.

Async Execution in RuleFlows →
Updated

Registry & Tracing Integration

  • RuleRegistry gains getRuleFlow(name) and getRuleFlows() — flows register like Rules and RuleSets
  • New RuleFlowListener fires Tracer events for flow and command start, completion, and errors
  • New BindingDeclaration.of(name, value) factory for runtime-known binding names
  • RuleExecutionStatus gains ERROR (and isError())
Rule Tracing & Debugging →
Updated

Correctness & Hardening Pass

A systematic review across every package; the test suite grew from 1039 to 1274 tests. Highlights:

  • Immutable bindings now actually enforce immutability; scope-shadowing fixes across the bind package
  • Numeric validation rules coerce strings via BigDecimal — decimal strings like "10.5" no longer falsely fail
  • Composite conditions short-circuit; text converters no longer misparse leading-zero strings as octal
  • Bounded internal executor with a JVM shutdown hook; thread-safety fixes in registry, script, trace, and util packages
  • RuleSet isAnyPass()/isAnySkip()/isAnyFail() inversion fixed; validating() and finalizer() now compose instead of overwriting each other
Spring

XML RuleFlows

The full RuleFlow command grammar is available as a Spring XML namespace — bind, run/async-run, when, for-each, scope, await, on-exception, and more — with script expressions substituting the builder's lambdas.

<r:ruleflow name="CheckoutFlow" returning="#ctx.status">
    <r:run name="ValidateOrderRule"/>
    <r:when condition="#ctx.valid == true">
        <r:then><r:execute>#ctx.status = 'priced'</r:execute></r:then>
        <r:otherwise><r:exit/></r:otherwise>
    </r:when>
</r:ruleflow>
XML RuleFlows →
Spring

Environment Placeholders & Terse Attributes

Script expressions may contain ${property:default} placeholders, resolved once at startup with @Value semantics. And simple rules collapse to one-liners with the new expression attributes:

<r:rule name="ApproveRule" given="#ctx.total >= ${order.minTotal:100}"
        then="#ctx.approved = true" otherwise="#ctx.approved = false"/>
XML Rule Definitions →
Spring

Executor Service & Listener Auto-Configuration

  • New rulii.executorService bean — a bounded thread pool used for async rule/flow execution; override it by defining a bean with that exact name
  • Tracer auto-configuration registers any RuliiListener, RuleListener, RuleSetListener, and RuleFlowListener beans found in the context
  • Converter precedence fixed: custom rulii converters → Spring ConversionService → rulii built-ins
  • Registry lookups inside flows resolve against the Spring registry at execution time
Auto-Configuration →