Repository navigation
fix(rules): stratify a rule above a not/or rule it calls (#628) - #631
Merged
Merged
Conversation
StratifiedEvaluator runs a stratum's positive-only rules to a fixpoint, then each rule with not / not-join / or / or-join once. stratify() gave a positive dependency the callee's stratum, so a rule calling a mixed rule ran before any of the callee's rows existed and returned too few rows, often none; a mixed caller ran before or after its callee depending on all_preds order. A positive edge to a predicate with a mixed rule now needs dep_stratum + 1, like a negative one, unless the callee reaches the caller: recursion through a mixed rule stays in one stratum and is not rejected as unstratifiable. has_mixed_clause is shared with the evaluator's partition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PtCdzbJMSS1V8qEPaJ4jXz
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Covers reaches() revisiting a predicate; the stratification coverage gate is 100%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PtCdzbJMSS1V8qEPaJ4jXz
This was referenced Oct 19, 2026
adityamukho
added a commit
that referenced
this pull request
Oct 19, 2026
Backport of #631. StratifiedEvaluator runs a stratum's positive-only rules to a fixpoint, then each rule with not / not-join / or / or-join once. stratify() gave a positive dependency the callee's stratum, so a rule calling a mixed rule ran before the callee's rows existed and returned too few rows, often none. A positive edge to a predicate with a mixed rule now needs dep_stratum + 1, unless the callee reaches the caller: recursion through a mixed rule stays in one stratum (#633) and is not rejected. Claude-Session: https://claude.ai/code/session_01PtCdzbJMSS1V8qEPaJ4jXz Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For #628.
Problem
StratifiedEvaluator::evaluateruns a stratum's positive-only rules to a fixpoint, then each rule withnot/not-join/or/or-joinonce.stratify()gave a positive dependency the callee's stratum, so a rule calling such a rule ran before any of the callee's rows existed and returned too few rows, often none. When the caller had its ownnot/or,all_predsorder decided, so asking for the callee first in the query changed the answer.Fix
A positive edge to a predicate with a mixed rule now needs
dep_stratum + 1, like a negative edge, unless the callee reaches the caller. In that case (recursion through a mixed rule) both stay in one stratum, so no rule set accepted before is now rejected with INT-054.has_mixed_clauseis shared with the evaluator's partition.Not covered: recursion through a mixed rule
A mixed rule still runs once per stratum, so recursion through one, directly (
reachcalling itself with anotin the body) or mutually (pcalls mixedq, which callsp), gives incomplete results today. A 5-node chain returns 7 of 10 reachability rows. This PR doesn't change that, and it isn't a regression. It needs a fixpoint loop over the stratum with deduplicated mixed-rule output, which is a separate issue.Tests
tests/not_join_test.rs: four A rule calling a rule with not / not-join returns too few rows #628 tests (a rule calling anot-joinrule with a free and a literal argument and through a forwarding rule; anotcallee; a mixed caller with the callee asked first or not; a rule calling anorrule). All four fail onv3.cargo test: 1588 passed, 0 failed, 10 ignored.cargo clippy --all-featuresand the wasm32browserclippy are clean.or-branch invocations or several rules per predicate. Its one finding is the recursion case above.🤖 Generated with Claude Code
https://claude.ai/code/session_01PtCdzbJMSS1V8qEPaJ4jXz