@prefix augo: <https://w3id.org/augmanitai/ns/oversight#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

augo:shape-Check a sh:NodeShape ;
    rdfs:label "Check" ;
    sh:node augo:shape-OversightAct ;
    sh:not [ sh:class augo:Release ],
        [ sh:class augo:Decision ],
        [ sh:class augo:Observation ] ;
    sh:property [ sh:maxCount 1 ;
            sh:message "A check needs one outcome."@en ;
            sh:minCount 1 ;
            sh:path augo:outcome ],
        [ sh:maxCount 1 ;
            sh:message "Exactly one named property per check; several properties are several checks."@en ;
            sh:minCount 1 ;
            sh:path augo:namedProperty ],
        [ sh:datatype xsd:dateTime ;
            sh:message "The outcome needs determined_at (date-time with timezone)."@en ;
            sh:minCount 1 ;
            sh:path ( augo:outcome augo:determinedAt ) ;
            sh:pattern "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$" ],
        [ sh:datatype xsd:dateTime ;
            sh:message "The named property needs stated_at (date-time with timezone)."@en ;
            sh:minCount 1 ;
            sh:path ( augo:namedProperty augo:statedAt ) ;
            sh:pattern "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$" ],
        [ sh:in ( augo:holds augo:doesNotHold augo:undetermined ) ;
            sh:message "The outcome value must be holds, does_not_hold or undetermined. not_performed is not a check and is never stored as holds."@en ;
            sh:minCount 1 ;
            sh:path ( augo:outcome augo:value ) ],
        [ sh:message "The named property needs a non-empty text."@en ;
            sh:minCount 1 ;
            sh:minLength 1 ;
            sh:path ( augo:namedProperty augo:text ) ;
            sh:pattern "\\S" ] ;
    sh:sparql [ a sh:SPARQLConstraint ;
            sh:message "The named property must be stated before the outcome is determined."@en ;
            sh:prefixes augo:shape-OntologyLink ;
            sh:select "SELECT $this WHERE { $this augo:namedProperty/augo:statedAt ?s ; augo:outcome/augo:determinedAt ?d . FILTER (!REGEX(STR(?s), \"(Z|[+-][0-9]{2}:[0-9]{2})$\") || !REGEX(STR(?d), \"(Z|[+-][0-9]{2}:[0-9]{2})$\") || !(?s < ?d)) }" ] ;
    sh:targetClass augo:Check .

augo:shape-Observation a sh:NodeShape ;
    rdfs:label "Observation" ;
    sh:node augo:shape-OversightAct ;
    sh:not [ sh:class augo:Check ],
        [ sh:class augo:Release ],
        [ sh:class augo:Decision ] ;
    sh:property [ sh:maxCount 0 ;
            sh:message "An observation has no named_property; with it the record describes another act."@en ;
            sh:path augo:namedProperty ],
        [ sh:maxCount 0 ;
            sh:message "An observation states no authority."@en ;
            sh:path augo:authorityRef ],
        [ sh:maxCount 0 ;
            sh:message "An observation has no permitted_action; with it the record describes another act."@en ;
            sh:path augo:permittedAction ],
        [ sh:maxCount 0 ;
            sh:message "An observation has no chosen; with it the record describes another act."@en ;
            sh:path augo:chosen ],
        [ sh:maxCount 0 ;
            sh:message "An observation has no outcome; with it the record describes a check."@en ;
            sh:path augo:outcome ],
        [ sh:maxCount 0 ;
            sh:message "An observation states no alternatives."@en ;
            sh:path augo:alternative ],
        [ sh:maxCount 0 ;
            sh:message "An observation states no authority."@en ;
            sh:path augo:authorityValidFrom ] ;
    sh:targetClass augo:Observation .

augo:shape-Release a sh:NodeShape ;
    rdfs:label "Release" ;
    sh:node augo:shape-Decision ;
    sh:property [ sh:datatype xsd:dateTime ;
            sh:lessThan augo:timestamp ;
            sh:message "The authority must apply before the release."@en ;
            sh:minCount 1 ;
            sh:path augo:authorityValidFrom ;
            sh:pattern "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$" ],
        [ sh:message "A release needs authority_ref (an IRI)."@en ;
            sh:minCount 1 ;
            sh:nodeKind sh:IRI ;
            sh:path augo:authorityRef ],
        [ sh:message "A release names the permitted action."@en ;
            sh:minCount 1 ;
            sh:minLength 1 ;
            sh:path augo:permittedAction ;
            sh:pattern "\\S" ] ;
    sh:targetClass augo:Release .

augo:shape-UndifferentiatedApproval a sh:NodeShape ;
    rdfs:label "UndifferentiatedApproval" ;
    rdfs:seeAlso <https://w3id.org/augmanitai/lexicon/undifferentiated-approval> ;
    sh:severity sh:Warning ;
    sh:sparql [ a sh:SPARQLConstraint ;
            sh:message "Undifferentiated approval: the record states human involvement but not which oversight act took place."@en ;
            sh:prefixes augo:shape-OntologyLink ;
            sh:select "SELECT $this WHERE { $this augo:humanInvolvement true . FILTER NOT EXISTS { $this a ?k . FILTER (?k IN (augo:Observation, augo:Check, augo:Decision, augo:Release)) } }" ] ;
    sh:targetSubjectsOf augo:humanInvolvement .

augo:shape-Decision a sh:NodeShape ;
    rdfs:label "Decision" ;
    sh:node augo:shape-OversightAct ;
    sh:not [ sh:class augo:Check ],
        [ sh:class augo:Observation ] ;
    sh:property [ sh:message "A decision needs the alternatives that were stated."@en ;
            sh:minCount 1 ;
            sh:path augo:alternative ],
        [ sh:maxCount 1 ;
            sh:message "A decision needs exactly one chosen alternative."@en ;
            sh:minCount 1 ;
            sh:path augo:chosen ],
        [ sh:datatype xsd:dateTime ;
            sh:lessThanOrEquals augo:timestamp ;
            sh:message "Alternatives must be stated no later than the decision."@en ;
            sh:minCount 1 ;
            sh:path augo:alternativesStatedAt ;
            sh:pattern "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$" ] ;
    sh:sparql [ a sh:SPARQLConstraint ;
            sh:message "The chosen option must be one of the stated alternatives."@en ;
            sh:prefixes augo:shape-OntologyLink ;
            sh:select "SELECT $this WHERE { $this augo:chosen ?c . FILTER NOT EXISTS { $this augo:alternative ?c } }" ] ;
    sh:targetClass augo:Decision .

augo:shape-OntologyLink a owl:Ontology ;
    dct:creator <https://orcid.org/0009-0006-3773-7796> ;
    dct:license <https://creativecommons.org/licenses/by/4.0/> ;
    dct:title "AUGMANITAI oversight record vocabulary — SHACL shapes"@en ;
    rdfs:comment "These shapes use SHACL-SPARQL (sh:sparql). A SHACL Core processor ignores those constraints and will accept records whose chosen value is not among the alternatives, whose named property was not stated before the outcome, and undifferentiated approvals. Date-times need an explicit timezone."@en ;
    rdfs:seeAlso <https://w3id.org/augmanitai/ns/oversight> ;
    owl:versionIRI <https://w3id.org/augmanitai/ns/oversight/0.1> ;
    sh:declare [ sh:namespace "https://w3id.org/augmanitai/ns/oversight#"^^xsd:anyURI ;
            sh:prefix "augo" ] .

augo:shape-OversightAct a sh:NodeShape ;
    rdfs:label "OversightAct" ;
    sh:property [ sh:message "An oversight act needs object_ref (an IRI)."@en ;
            sh:minCount 1 ;
            sh:nodeKind sh:IRI ;
            sh:path augo:objectRef ],
        [ sh:hasValue augo:naturalPerson ;
            sh:maxCount 1 ;
            sh:message "An oversight act needs overseer_kind = natural_person."@en ;
            sh:minCount 1 ;
            sh:path augo:overseerKind ],
        [ sh:datatype xsd:dateTime ;
            sh:maxCount 1 ;
            sh:message "An oversight act needs one timestamp (date-time with timezone, Z or ±hh:mm)."@en ;
            sh:minCount 1 ;
            sh:path augo:timestamp ;
            sh:pattern "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$" ] ;
    sh:targetClass augo:OversightAct .

