@prefix sh:    <http://www.w3.org/ns/shacl#> .
@prefix vso:   <https://w3id.org/vson/v1/ontology#> .
@prefix vss:   <https://w3id.org/vson/v1/shapes#> .
@prefix rcc:   <https://w3id.org/vson/v1/rcc8#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:   <http://www.w3.org/2002/07/owl#> .
@prefix dc:    <http://purl.org/dc/terms/> .
@prefix vann:  <http://purl.org/vocab/vann/> .

# The document IRI names the shapes graph itself, not a shape inside it. It was
# typed sh:NodeShape until v1.1 — inert at validation time (a node shape with no
# sh:target* and no constraints selects nothing), but wrong as metadata: it told
# every reader and every vocabulary tool that this file's identity is a shape.
# owl:Ontology is what the declaration always meant.
<https://w3id.org/vson/v1/shapes-relaxed>
    a owl:Ontology ;
    dc:title "VSON-S shapes (relaxed profile)" ;
    dc:creator "yamancan" ;
    dc:license <https://www.apache.org/licenses/LICENSE-2.0> ;
    dc:modified "2026-07-31"^^xsd:date ;
    owl:versionInfo "1.2" ;
    vann:preferredNamespacePrefix "vss" ;
    vann:preferredNamespaceUri "https://w3id.org/vson/v1/shapes#" ;
    rdfs:label "VSON v1.2 SHACL shapes — relaxed profile" ;
    rdfs:comment "Relaxed validation profile for partial / authoring-time documents. Carries the same shapes as vson-shapes.ttl (strict), but downgrades to sh:Warning the dimension-completeness constraints: Talmy viewer, Event/Process/Stative lemma, Quality dimension/value, Quality modifier. Structural-integrity constraints (Composition.depicts, FrameNotDepicted, SpatialFact.figure/ground, RCC/directional/proximal value vocab, Negation/BeliefState/Quantification/Annotation slots, Persona.hasInvariant, trait enums) remain sh:Violation. No shipped command selects this profile yet; the test suite exercises it via tools/shacl_helper (tests/test_shapes_gate.py). Documents that conform under strict also conform under relaxed (no strict shape was made stricter)." .

#################################################################
# NB — why the sh:class checks below carry sh:not guards
#
# Mirrors the note at the top of shapes/vson-shapes.ttl. Validation runs with
# inference="rdfs", so a sh:class naming the property's own rdfs:range is
# entailed onto every value node and can never fail on its own. The sh:not
# guards name the classes ontology/vso.ttl declares disjoint from that range, so
# a wrongly-typed value carries both its asserted type and the entailed range
# type and the guard fires. Severities follow this profile's usual split.
#################################################################

#################################################################
# STRUCTURAL FLOOR — sh:Violation in both profiles
#################################################################

vss:CompositionShape a sh:NodeShape ;
    sh:targetClass vso:Composition ;
    sh:property [
        sh:path vso:depicts ;
        sh:minCount 1 ;
        sh:message "A Composition must depict at least one Entity (vso:depicts)."
    ] ;
    sh:property [
        sh:path vso:framedBy ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:class vso:Frame ;
        sh:not [ sh:class vso:Entity ] ;
        sh:message "vso:framedBy must reference a Frame (SceneContext / VisualStyle / CameraView / Persona), never an Entity."
    ] .

vss:SpatialFactShape a sh:NodeShape ;
    sh:targetClass vso:SpatialFact ;
    sh:property [
        sh:path vso:figure ; sh:minCount 1 ; sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI
    ] ;
    sh:property [
        sh:path vso:ground ; sh:minCount 1 ; sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI
    ] ;
    sh:or (
        [ sh:property [ sh:path vso:rcc ;         sh:minCount 1 ] ]
        [ sh:property [ sh:path vso:directional ; sh:minCount 1 ] ]
        [ sh:property [ sh:path vso:proximal ;    sh:minCount 1 ] ]
    ) ;
    sh:message "SpatialFact must specify at least one of vso:rcc, vso:directional, or vso:proximal." .

vss:RccValueShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:rcc ;
    sh:property [
        sh:path vso:rcc ;
        sh:in ( rcc:DC rcc:EC rcc:PO rcc:EQ rcc:TPP rcc:NTPP rcc:TPPi rcc:NTPPi ) ;
        sh:message "vso:rcc value must be one of the eight RCC-8 relations."
    ] .

vss:DirectionalValueShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:directional ;
    sh:property [
        sh:path vso:directional ;
        sh:in ( vso:above vso:below vso:left_of vso:right_of vso:in_front_of vso:behind ) ;
        sh:message "vso:directional value must be one of the six frame-relative directions."
    ] .

vss:ProximalValueShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:proximal ;
    sh:property [
        sh:path vso:proximal ;
        sh:in ( vso:near vso:far vso:adjacent vso:next_to vso:facing ) ;
        sh:message "vso:proximal value must be one of the five proximity relations."
    ] .

vss:FrameNotDepictedShape a sh:NodeShape ;
    sh:targetObjectsOf vso:depicts ;
    sh:not [ sh:class vso:Frame ] ;
    sh:message "vso:depicts must point to an Entity, not a Frame. Use vso:framedBy for the perspectival layer." .

# sh:class vso:QualityBearer would be vacuous under inference="rdfs" (it is the
# rdfs:domain of vso:hasQuality). Assert the concrete subclasses instead.
vss:HasQualityShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:hasQuality ;
    sh:or ( [ sh:class vso:Entity ] [ sh:class vso:Composition ] ) ;
    sh:message "Only a QualityBearer (an Entity, or a Composition bearing compositional qualities) may carry vso:hasQuality." .

vss:NegationShape a sh:NodeShape ;
    sh:targetClass vso:Negation ;
    sh:property [
        sh:path vso:negatedStatement ;
        sh:minCount 1 ;
        sh:maxCount 1
    ] .

# Structural-integrity shapes for the reification nodes — a reified node missing
# its required slots is malformed, not merely partial, so these stay Violation
# in both profiles. (Previously absent from the relaxed file, contradicting its
# "same shapes as strict" header.)
vss:BeliefStateShape a sh:NodeShape ;
    sh:targetClass vso:BeliefState ;
    sh:property [
        sh:path vso:experiencer ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:class vso:Endurant ;
        sh:not [ sh:class vso:Perdurant ] ;
        sh:not [ sh:class vso:Frame ] ;
        sh:message "BeliefState must have exactly one experiencer, and it must be an Endurant — not a Perdurant or a Frame."
    ] ;
    sh:property [
        sh:path vso:proposition ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "BeliefState must reference exactly one proposition (quoted triple or Annotation node)."
    ] .

vss:QuantificationShape a sh:NodeShape ;
    sh:targetClass vso:Quantification ;
    sh:property [
        sh:path vso:quantifier ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "all" "every" "some" "no" "most" "few" ) ;
        sh:message "Quantification must have exactly one quantifier from the closed list (all|every|some|no|most|few)."
    ] ;
    sh:property [
        sh:path vso:variable ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "Quantification must declare exactly one bound variable name."
    ] ;
    sh:property [
        sh:path vso:scope ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "Quantification must declare exactly one scope proposition."
    ] .

vss:AnnotationShape a sh:NodeShape ;
    sh:targetClass vso:Annotation ;
    sh:property [
        sh:path vso:annotatedSubject ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "Annotation must declare exactly one annotatedSubject."
    ] ;
    sh:property [
        sh:path vso:annotatedPredicate ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "Annotation must declare exactly one annotatedPredicate."
    ] ;
    sh:property [
        sh:path vso:annotatedObject ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:message "Annotation must declare exactly one annotatedObject."
    ] .

# depicts-discipline (Warning, matching strict): reification nodes belong on hasFact.
vss:DepictsEntityShape a sh:NodeShape ;
    sh:targetObjectsOf vso:depicts ;
    sh:class vso:Entity ;
    sh:severity sh:Warning ;
    sh:message "vso:depicts SHOULD point to an Entity; attach reification nodes via vso:hasFact." .

vss:PersonaShape a sh:NodeShape ;
    sh:targetClass vso:Persona ;
    sh:property [
        sh:path vso:hasInvariant ;
        sh:minCount 1 ;
        sh:class vso:Quality ;
        # NOT vso:Entity: Quality is rdfs:subClassOf vso:Entity, so an Entity
        # guard would fire on every well-formed Persona. See the strict file.
        sh:not [ sh:class vso:Endurant ] ;
        sh:not [ sh:class vso:Perdurant ] ;
        sh:not [ sh:class vso:Region ] ;
        sh:not [ sh:class vso:Frame ] ;
        sh:message "Persona must declare at least one hasInvariant Quality; an invariant may not be an Endurant, Perdurant, Region, or Frame."
    ] .

#################################################################
# RELAXED — sh:Warning in this profile; sh:Violation under strict
#################################################################

# Talmy viewer — relaxed in this profile (severity on property shape, not
# NodeShape). The sh:not guard that de-vacuates sh:class rides the same property
# shape, so it too reports at sh:Warning here: this profile downgrades the whole
# Talmy-viewer constraint, and splitting it would silently make one half of an
# existing shape stricter than the strict-to-relaxed contract in the header.
vss:DirectionalNeedsViewerShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:directional ;
    sh:property [
        sh:path vso:viewer ;
        sh:minCount 1 ;
        sh:class vso:CameraView ;
        sh:not [ sh:class vso:Entity ] ;
        sh:severity sh:Warning ;
        sh:message "Directional spatial facts SHOULD have a vso:viewer, and that viewer SHOULD be a CameraView, never an Entity. (Relaxed-profile warning.)"
    ] .

# Event / Process / Stative lemma — relaxed
vss:EventShape a sh:NodeShape ;
    sh:targetClass vso:Event ;
    sh:property [
        sh:path vso:lemma ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:severity sh:Warning ;
        sh:message "Event SHOULD have exactly one vso:lemma. (Relaxed-profile warning.)"
    ] ;
    sh:property [
        sh:path vso:agent ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:maxCount 1 ;
        sh:class vso:Endurant ;
        sh:not [ sh:class vso:Perdurant ] ;
        sh:not [ sh:class vso:Frame ] ;
        sh:message "vso:agent must reference an Endurant, not a Perdurant or a Frame."
    ] .

vss:ProcessShape a sh:NodeShape ;
    sh:targetClass vso:Process ;
    sh:property [ sh:path vso:lemma ; sh:datatype xsd:string ; sh:minCount 1 ;
                  sh:severity sh:Warning ] .

vss:StativeShape a sh:NodeShape ;
    sh:targetClass vso:Stative ;
    sh:property [ sh:path vso:lemma ; sh:datatype xsd:string ; sh:minCount 1 ;
                  sh:severity sh:Warning ] ;
    sh:property [ sh:path vso:experiencer ; sh:maxCount 1 ; sh:class vso:Endurant ;
                  sh:not [ sh:class vso:Perdurant ] ;
                  sh:not [ sh:class vso:Frame ] ;
                  sh:message "vso:experiencer must reference an Endurant, not a Perdurant or a Frame." ] .

# Quality dim/value — relaxed
#
# No sh:in on vso:dimension here either, for the reason spelled out beside the
# strict QualityShape in shapes/vson-shapes.ttl: the VSO dimension registry is
# closed only within the VSO namespace, document-namespace dimensions stay
# conformant, and the closed half is enforced by owl:AllDifferent in
# ontology/vso.ttl plus the C2 coverage test in tests/test_shapes_gate.py.
vss:QualityShape a sh:NodeShape ;
    sh:targetClass vso:Quality ;
    sh:property [
        sh:path vso:dimension ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:severity sh:Warning
    ] ;
    sh:property [
        sh:path vso:value ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:severity sh:Warning
    ] .

# Quality modifier — relaxed to Warning here (cardinality 0..1; absence is normal).
vss:QualityModifierShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:modifier ;
    sh:property [
        sh:path vso:modifier ;
        sh:datatype xsd:string ;
        sh:maxCount 1 ;
        sh:severity sh:Warning ;
        sh:message "A Quality carries at most one vso:modifier (optional adverbial bareword). (Relaxed-profile warning.)"
    ] .

# Persona embodiment consistency — already Warning in strict; identical here
vss:EmbodimentConsistencyShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:embodies ;
    sh:severity sh:Warning ;
    sh:sparql [
        sh:select """
            PREFIX vso: <https://w3id.org/vson/v1/ontology#>
            SELECT $this ?dim ?invariantValue ?embodiedValue WHERE {
                $this vso:embodies ?persona .
                ?persona vso:hasInvariant ?invQ .
                ?invQ vso:dimension ?dim ;
                      vso:value     ?invariantValue .
                $this vso:hasQuality ?embQ .
                ?embQ vso:dimension ?dim ;
                      vso:value     ?embodiedValue .
                FILTER (?invariantValue != ?embodiedValue)
            }
        """ ;
        sh:message "Entity hasQuality value contradicts embodied Persona's hasInvariant on the same dimension."
    ] .

#################################################################
# Trait enums — sh:Violation in both profiles (closed vocab)
#################################################################

vss:IndividuationShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:individuation ;
    sh:property [
        sh:path vso:individuation ;
        sh:in ( vso:Generic vso:Named vso:Kind vso:Skolem ) ;
        sh:maxCount 1
    ] .

vss:AnimacyShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:animacy ;
    sh:property [
        sh:path vso:animacy ;
        sh:in ( vso:Agentive vso:Inert ) ;
        sh:maxCount 1
    ] .

vss:CountabilityShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:countability ;
    sh:property [
        sh:path vso:countability ;
        sh:in ( vso:Count vso:Mass vso:Collective ) ;
        sh:maxCount 1
    ] .

# Fourth trait axis; non-functional (0..n), hence no sh:maxCount. Parity with the
# strict file.
vss:AffordanceShape a sh:NodeShape ;
    sh:targetSubjectsOf vso:affordance ;
    sh:property [
        sh:path vso:affordance ;
        sh:in ( vso:Holdable vso:Wearable vso:Mountable vso:Container vso:Edible ) ;
        sh:message "vso:affordance value must be one of the five published affordances (Holdable|Wearable|Mountable|Container|Edible)."
    ] .
