Skip to content

Backward chaining does not resolve fact-to-fact comparisons #1

Description

@ops-ping

Backward chaining silently fails any rule condition that compares two facts, while forward chaining handles the same condition correctly.

Evidence

Through vrules_core::prove (which delegates to rust_rule_engine::backward::{BackwardEngine, GRLQueryParser, GRLQueryExecutor}):

knowledge base result
when Order.Amount <= Account.Balance not provable, 0 proof steps
when Order.Amount <= 100.0 provable, 1 proof step

Facts {"Order.Amount": 50, "Account.Balance": 100, "Account.AgeDays": 45} in both cases.

Forward chaining, same shape, is correct in all four quadrants (Order.amount > Order.limit):

amount limit engine truth
100 50 true true
50 100 false false
1 999 false false
999 1 true true

Cause

src/backward/rule_executor.rs delegates to engine::condition_evaluator::ConditionEvaluator, whose evaluate_condition compares the resolved left field against condition.value verbatim:

Ok(condition.operator.evaluate(&value, &condition.value))

The right-hand operand is never resolved against facts, so Account.Balance is compared as the literal string "Account.Balance". The forward engine has its own path in src/engine/engine.rs including resolve_value_facts(val, facts), which is exactly the resolution the backward path lacks.

This is an asymmetry in upstream rust-rule-engine, not something vrules removed: condition_evaluator.rs and the whole backward/ tree are byte-identical to rust-rule-engine.pre-purge-backup-20260802-130744.

Blast radius

ConditionEvaluator is referenced only from src/backward/ (rule_executor.rs, search.rs), so a fix is contained to backward chaining.

Impact

The Proof browser example ships a knowledge base whose first rule uses Order.Amount <= Account.Balance, so it renders NOT PROVABLE with an empty proof tree, and has been doing so on the published site.

Coverage gap

crates/vrules-core/tests/conformance_backward.rs (43 tests) contains no fact-to-fact comparison at all, which is why this was never caught.

Suggested fix

Resolve condition.value against facts before comparing, mirroring resolve_value_facts, and add backward conformance coverage for fact-to-fact in all four comparison quadrants.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions