Skip to content

A rule computes what it concludes - #495

Merged
WaylandYang merged 2 commits into
devfrom
feat/a-rule-computes-cut2
Sep 9, 2026
Merged

WaylandYang merged 2 commits into
devfrom
feat/a-rule-computes-cut2

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Cut 2 of #488, on top of the record (#493). Stacked on feat/a-rule-computes.

Expr in utopia-reasonAttr(predicate) | Const(f64) | Arith { op, l, r }, depth capped by MAX_EXPR_DEPTH. eval returns Option<f64> and returns None for a reading that is not there, a value that is not a number, division by zero, and any non-finite result. Never zero: a missing reading is nobody having written it down, and filling that in with a definite answer is the one thing this ledger must not do.

A third conclusion kind, computed (migration 0041), with the tree in attribute_rules.conclude_expr and the shape CHECK extended to three branches. The two existing branches are untouched, and a {"const": …} is what every rule stores today, so nothing migrates.

An operand may be an expression on the comparison ops, so a threshold can be computed from another reading: revenue > cost × 1.5. No column for it — operand is already JSONB, and the four operand shapes are distinguishable (a number, a two-element array, a string array, an object).

The evaluator now builds the combination before testing the conditions. It has to: a computed threshold reads whichever readings this round picked, so a condition can no longer be decided against one fact in isolation. Slots are one per condition (unchanged — two conditions on the same attribute still pick independently) plus one per predicate an expression reads that no condition covers; an expression referring to a predicate a condition already has binds to that same slot, so thc > thc × 0.5 means one reading on both sides. Every attribute the expression touched is a premise, which is what makes a computed conclusion able to say why it holds and to retire when any of those readings changes.

Dedupe is now by (interval, value) rather than interval alone — two different computed values on one interval are two rows, not one.

A malformed tree is refused where it is written. validate_expr checks the shape, the depth, that every attribute exists in the base and is an attribute predicate, and that the tree reads at least one attribute (an expression that reads none is a constant, and there is already a branch for that). This is deliberate and it is the lesson of #494: a tree the compiler cannot read would otherwise mean "this rule silently concludes nothing" with nothing anywhere saying why.

Tests. Seven in the evaluator — the readings an expression touched become premises; each combination computes its own value; a missing reading computes nothing; division by zero computes nothing; a computed threshold works and its reading is a premise; two values on one interval are two hits; depth counts the deepest branch. Three against a real database — the round trip through create lands 180 with both readings as premises and then follows cost down to 250 on the next run; a missing reading lands nothing; four kinds of broken expression are refused at write time.

cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings and the full suite (72 test binaries) are clean.

Not in this cut: the picker (an expression is reachable through the API, not through the page) and reaching an attribute across a relation, which the record decides and does not build — the evaluator reads only the literal channel today, and wiring the entity–entity channel into it is the bulk of that work.

🤖 Generated with Claude Code

Base automatically changed from feat/a-rule-computes to dev September 9, 2026 02:26
Signed-off-by: WaylandYang <wayland0916@gmail.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang force-pushed the feat/a-rule-computes-cut2 branch from b90b24a to f68bede Compare September 9, 2026 02:26
@WaylandYang
WaylandYang merged commit 3c33a52 into dev Sep 9, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the feat/a-rule-computes-cut2 branch September 9, 2026 02:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant