Constraints and Validation
Defining what valid data looks like and how validation drives autonomous data governance.
Constraints define the rules your data must follow. They're declared as SHACL shapes in shapes.ttl and enforced both during assembly (when your matrix is built) and continuously at runtime (as data changes). This guide walks through designing effective validation strategies for different scenarios, from simple required fields to multi-level data quality checks.
Why Constraints Matter for AI
Constraints are not just for data integrity. They're how RARS knows whether its work is correct. When RARS creates a task, the constraints tell it whether the result is valid. When it updates a customer record, the constraints catch missing fields or invalid values before the data reaches your graph. This is a core part of what makes RARS pseudo-deterministic: even when AI reasoning produces the output, your constraints verify it conforms to your business rules.
RARS also uses constraint violations for self-correction. When a validation finding surfaces, RARS can read the constraint message, understand what went wrong, and attempt to fix it. The quality of your constraint messages directly affects how well RARS can autonomously maintain data quality.
A Simple Constraint
Start with the most common case: a class where certain properties are required.
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix tasks: <https://example.org/spec/tasks#> .
tasks:TaskShape
a sh:NodeShape ;
dct:description "Validates that every task has a title and a status." ;
sh:targetClass tasks:Task ;
sh:property tasks:Task-title ;
sh:property tasks:Task-status .
tasks:Task-title
a sh:PropertyShape ;
sh:path tasks:title ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:severity sh:Violation ;
sh:message "A task must have exactly one title." ;
dct:description "The title of the task." .
tasks:Task-status
a sh:PropertyShape ;
sh:path tasks:status ;
sh:class tasks:TaskStatus ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:severity sh:Violation ;
sh:message "A task must have exactly one status." ;
dct:description "The current lifecycle status of the task." .This says: every tasks:Task instance must have exactly one title (a string) and exactly one status (a tasks:TaskStatus individual). If either is missing or duplicated, it's a violation.
For the complete list of SHACL constraint properties, see the SHACL specification.
Writing Good Constraint Messages
The sh:message on each constraint is what RARS sees when validation fails. Write messages that explain both the rule and the expectation:
- Bad: "Validation error." (RARS can't self-correct from this)
- OK: "Title is required." (RARS knows what's missing)
- Good: "A task must have exactly one title." (RARS knows the exact cardinality expectation)
For constraints that restrict values to a set, include the valid options:
tasks:Task-priority
a sh:PropertyShape ;
sh:path tasks:priority ;
sh:in ("low" "medium" "high" "critical") ;
sh:message "Task priority must be one of: low, medium, high, critical." .Multi-Level Severity
Not every constraint violation is an error. SHACL supports three severity levels that map to how findings are classified in validation reports and how RARS responds to them.
Violations: Hard Errors
Use sh:Violation for rules that represent genuinely invalid data. These block the data from being trusted:
tasks:Task-title
a sh:PropertyShape ;
sh:path tasks:title ;
sh:minCount 1 ;
sh:severity sh:Violation ;
sh:message "A task must have a title." .Warnings: Data Quality Signals
Use sh:Warning for rules where the data is technically valid but might indicate a problem. RARS treats these as signals to investigate, not blockers:
tasks:Task-description
a sh:PropertyShape ;
sh:path tasks:description ;
sh:minCount 1 ;
sh:severity sh:Warning ;
sh:message "Tasks without a description are harder for team members to understand." .
tasks:Task-assignee
a sh:PropertyShape ;
sh:path tasks:assignee ;
sh:minCount 1 ;
sh:severity sh:Warning ;
sh:message "Unassigned tasks may not be worked on. Consider assigning an owner." .Info: Observations
Use sh:Info for rules that are purely informational. RARS notes these but doesn't treat them as problems:
tasks:Task-tags
a sh:PropertyShape ;
sh:path tasks:tags ;
sh:minCount 1 ;
sh:severity sh:Info ;
sh:message "Adding tags improves searchability." .Combining Severities
A well-designed constraint set uses all three levels. Required fields are violations. Recommended practices are warnings. Nice-to-haves are info. This gives RARS a nuanced understanding of data quality rather than a binary pass/fail.
Constraining Values
Beyond cardinality, you can constrain what values are acceptable.
Enumerated Values
Restrict a property to a fixed set of values:
crm:Deal-stage
a sh:PropertyShape ;
sh:path crm:stage ;
sh:in (crm:Prospecting crm:Qualification crm:Proposal crm:Negotiation crm:ClosedWon crm:ClosedLost) ;
sh:message "Deal stage must be a valid pipeline stage." .Use named individuals (URIs) for values that have meaning in your domain, not string literals. This allows RARS to reason about the values and follow relationships from them.
Pattern Matching
Enforce format constraints with regular expressions:
crm:Account-accountNumber
a sh:PropertyShape ;
sh:path crm:accountNumber ;
sh:pattern "^ACC-[0-9]{6}$" ;
sh:message "Account number must follow the pattern ACC-NNNNNN (e.g., ACC-001234)." .Type Constraints
Use sh:datatype for literal properties and sh:class for object properties:
# Literal: must be a date
billing:Invoice-dueDate
a sh:PropertyShape ;
sh:path billing:dueDate ;
sh:datatype xsd:date ;
sh:minCount 1 ;
sh:message "Every invoice must have a due date (xsd:date)." .
# Object: must be an instance of Customer
billing:Invoice-customer
a sh:PropertyShape ;
sh:path billing:customer ;
sh:class crm:Customer ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:message "Every invoice must reference exactly one customer." .Constraint Inheritance
When a class has subclasses, constraints follow the hierarchy. If you constrain tasks:Task, then tasks:BugReport (a subclass of tasks:Task) is also validated against those constraints. You don't need to redeclare inherited constraints.
If a subclass needs stricter rules, add a shape targeting the subclass. Both the parent and child constraints apply:
# All tasks need a title
tasks:TaskShape
a sh:NodeShape ;
sh:targetClass tasks:Task ;
sh:property tasks:Task-title .
# Bug reports additionally need severity and steps to reproduce
tasks:BugReportShape
a sh:NodeShape ;
sh:targetClass tasks:BugReport ;
sh:property tasks:BugReport-severity ;
sh:property tasks:BugReport-stepsToReproduce .A tasks:BugReport instance is validated against both shapes: it needs a title (from TaskShape) and severity plus steps to reproduce (from BugReportShape).
Open vs. Closed Shapes
By default, shapes are open: instances can have properties beyond what the shape defines. This is usually what you want. Your constraints define the minimum valid structure; additional properties are allowed.
Use sh:closed true only when you need strict control over exactly what properties an instance can have. This is rare and should be reserved for cases where unexpected properties could cause problems (e.g., sensitive data types where stray properties might leak information).
Design Principles
Constrain Inputs, Not Intermediaries
Focus constraints on the data that enters your graph from external sources (API responses, user input, imported data). Intermediate computations and internal state are managed by RARS and generally don't need constraints.
Start Strict, Loosen Later
It's easier to relax a constraint (change a Violation to a Warning) than to tighten one (data that was valid yesterday is now invalid). Start with required fields and strict types. Add flexibility when you discover real-world data that legitimately needs it.
Message for the AI, Not Just Humans
RARS reads constraint messages during self-correction. A message like "Invalid data" gives it nothing to work with. A message like "Every invoice must reference exactly one customer (sh:class crm:Customer)" tells RARS exactly what the expected shape of the data is and what type to use.
See Also
- Domain Modeling: designing the classes and properties that constraints apply to
- Provenance: how validation findings are tracked in the observation model
- Service Integrations: constraints on action payloads and results
- SHACL Specification: the W3C standard for Shapes Constraint Language
- SHACL Advanced Features: SPARQL-based constraints, rules, and custom targets