13.2 What a rule may say — the validated fragment
Validation at install time enforces the fragment that makes hosted rules safe — each restriction is a leg of the guarantee that evaluation terminates:
| You may | You may not |
|---|---|
| conclude a label or a relationship, with derived properties | write the graph (CREATE/MERGE/SET/DELETE in a body) |
reference the derived label positively — recursion, any(…), EXISTS |
reference it under a negation: NOT, or the quantifiers none(…) / all(…) / single(…) |
aggregate in recursion with sum / count / max / collect |
use min / avg / percentiles inside recursion |
| reference other labels, views and virtual joins as usual | RETURN in a body — the head is the projection |
reference declared params as $name |
reference a param the set does not declare |
| conclude the same kind in every rule of a set | mix a node head and a relationship head — one fixpoint cannot be both |
declare requires: to bring the facts in first (§13.6) |
write the graph in a demand — it is read-only like a body |
none(…) is NOT any(…), and both all(…) and single(…) can be falsified by a member a later
round adds — all three are the same non-monotone self-reference as NOT, whatever they look like,
so all three are refused. Say it the monotone way instead: any(…) and EXISTS only become more
true as membership grows, which is exactly what the fixpoint needs.
The rule is monotonicity, not the word NOT, and it is checked position by position: a reference to
the derived label must sit where a new member can only make the body MORE true. A negation in any
other spelling is refused the same way — a count compared from above (size([x IN xs WHERE x:Derived]) = 0, COUNT { (p)-[:R]->(:Derived) } <= 3), a CASE that inverts the label, an
OPTIONAL MATCH of it followed by IS NULL, a SKIP/LIMIT a new member could push a row out of.
Count members from below instead (>= n), or use any(…)/EXISTS; sum(CASE WHEN x:Derived THEN 1 ELSE 0 END) >= n is admitted because it only grows. Such a rule would otherwise settle at once on a
set its own body contradicts, which the round cap cannot notice because nothing is still moving — so
a body the host cannot prove monotone is refused rather than trusted.
A rule set that breaks the fragment fails validation with a message naming the rule and the
property it broke; it never half-installs. A conforming set’s MEMBERSHIP is guaranteed to
terminate: rules can only conclude over nodes that exist, membership only grows, and a growing
subset of a finite set must stop growing. For a relationship set the finite set is the square
of the node count, which is why it carries its own budget (maxPairs, §13.4) on top of the
argument.
That argument is about membership, and it does not extend to a derived NUMERIC PROPERTY defined
in terms of its own value on a neighbour. phoenixDepth above is one: it settles because
appointment dates strictly increase, so the chain it climbs cannot close. On CYCLIC data — mutual
blocks in a tracker, cross-holdings in an ownership graph, dependency cycles in a registry — two
members lift each other by one every round and there is no ceiling for the fixpoint to stop at.
The evaluation then fails loudly at its round cap, naming the property that would not settle and
the rule that stamps it; it never truncates silently. Note that the aggregate is not what decides
this — max cannot shrink as membership grows, so the fragment admits it, while a grounded min
descending to a floor would converge and is refused; what bounds such a value is groundedness,
which lives in your data and not in the rule. Conclude membership in the rule set and compute a
distance in the projection view (§13.5), where min(length(p)) is finite because a path may not
repeat a relationship.
A rule body’s labels are checked when the realm is validated. A body label that is a near miss
for a known one (Compnay beside Company) is an error at the rule’s from, naming the likely
intended label: a misspelled body matches nothing, so the rule would derive nothing and say nothing.

