โ† All docs  ยท  Home

ADR 0009 โ€” Custom rating scales via sh:lessThanOrEquals

Context

ADR 0004 validated ratings against fixed scales (stars 1..5, scalar 0..1, thumb {0,1}) and noted that validating a value against the body's own worstRating/bestRating "needs a sibling-property comparison that SHACL Core cannot express." That was wrong: SHACL Core does have property-pair constraints โ€” sh:lessThanOrEquals / sh:lessThan compare the values of the shape's path against the values of another property on the same focus node. No SHACL-SPARQL required.

Decision

freedback:ScalarRating carries its own scale, so validate it relative to that scale:

worstRating โ‰ค ratingValue โ‰ค bestRating

expressed in shapes.ttl as two sh:lessThanOrEquals property shapes (worstRating โ‰ค ratingValue and ratingValue โ‰ค bestRating), plus the datatype and required-ness checks. The default scalar scale stays 0..1 (the body's default bounds), but any worst < best is accepted โ€” a 0..10 scalar with value 7 now validates.

StarRating (1..5) and ThumbRating ({0,1}) keep their fixed canonical scales โ€” they are not free-scale by design โ€” so this change is scoped to scalar ratings.

The in-house validator gained a sh:lessThanOrEquals evaluator (a few lines): it compares the path's numeric values to the referenced sibling property's values on the same focus node, exactly per the SHACL Core semantics.

Why this is still "validation lives in SHACL"

The rule is expressed entirely in shapes.ttl with a standard SHACL Core constraint component; the validator just interprets it. Swapping in a full external SHACL engine later would interpret the same shapes identically.

Consequences