@prefix : <https://ontology.inferal.com/modules/ontology-migration/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix mod: <https://w3id.org/mod#> .
@prefix ord: <https://ontology.inferal.com/modules/ordering/> .
@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 qt: <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
@prefix vann: <http://purl.org/vocab/vann/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://ontology.inferal.com/modules/ontology-migration/shapes>
    a owl:Ontology ;
    dcterms:title "Ontology Data Migration shapes" ;
    dcterms:description "Closed-world validation contract for explicitly scoped SHACL-AF ontology migration sets, mandatory source and target validation gates, reverse-migration endpoint agreement, optional paired verification tests, meaningful rule dependencies, and staged retraction planning." ;
    dcterms:license <https://creativecommons.org/licenses/by/4.0/> ;
    dcterms:source : ;
    vann:preferredNamespacePrefix "om" ;
    vann:preferredNamespaceUri "https://ontology.inferal.com/modules/ontology-migration/" ;
    mod:status "draft" ;
    rdfs:label "Ontology Data Migration shapes" ;
    rdfs:comment "Closed-world validation contract for one or more ontology migration sets in an assembled RDF graph. It requires one source and one target shapes graph for each execution-ready set, validates swapped endpoints for declared reverse migrations, and checks migration-specific pairings of W3C query-evaluation tests. Explicit case and retraction-rule relations define set-specific execution scope." ;
    owl:versionInfo "0.1.0" .

:OntologyMigrationSetShape
    a sh:NodeShape ;
    rdfs:label "ontology migration set shape" ;
    rdfs:comment "Validates one source ontology, one distinct target ontology, exactly one source and target shapes graph, exactly one execution shapes graph, at least one explicitly associated transformation case, declared reverse endpoints, and meaningful rule ordering." ;
    sh:targetClass :OntologyMigrationSet ;
    sh:nodeKind sh:IRI ;
    sh:property [
        sh:path :sourceOntology ;
        sh:class owl:Ontology ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :targetOntology ;
        sh:class owl:Ontology ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path [
            sh:alternativePath (
                :hasCase
                [ sh:inversePath :caseOf ]
            )
        ] ;
        sh:node :TransformationCaseShape ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path :sourceShapesGraph ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :targetShapesGraph ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :executionShapesGraph ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :reverseMigration ;
        sh:class :OntologyMigrationSet ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:sparql [
        sh:message "The source and target ontologies of a migration set must be distinct RDF terms." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            SELECT $this WHERE {
                $this om:sourceOntology ?version ;
                      om:targetOntology ?version .
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "Every declared reverse migration must swap this migration set's source and target ontology endpoints." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            SELECT $this ?reverseMigration WHERE {
                $this om:sourceOntology ?sourceOntology ;
                      om:targetOntology ?targetOntology ;
                      om:reverseMigration ?reverseMigration .
                FILTER NOT EXISTS {
                    ?reverseMigration om:sourceOntology ?targetOntology ;
                                      om:targetOntology ?sourceOntology .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A rule with sh:order must also participate in an ord:precedes or ord:follows dependency; numeric order alone does not state why ordering matters." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            PREFIX sh: <http://www.w3.org/ns/shacl#>
            SELECT DISTINCT $this ?rule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                {
                    ?case sh:rule ?rule .
                }
                UNION
                {
                    ?case (om:hasRetractionRule | ^om:retractionRuleOf) ?rule .
                }
                ?rule sh:order ?order .
                FILTER NOT EXISTS {
                    ?rule (ord:precedes | ord:follows | ^ord:precedes | ^ord:follows) ?otherRule .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "An ordered additive rule may precede or follow only another additive rule in the same migration set." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            PREFIX sh: <http://www.w3.org/ns/shacl#>
            SELECT DISTINCT $this ?rule ?otherRule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                ?case sh:rule ?rule .
                ?rule (ord:precedes | ord:follows | ^ord:precedes | ^ord:follows) ?otherRule .
                FILTER NOT EXISTS {
                    $this (om:hasCase | ^om:caseOf) ?otherCase .
                    ?otherCase sh:rule ?otherRule .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "An ordered retraction rule may precede or follow only another retraction rule in the same migration set." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            SELECT DISTINCT $this ?rule ?otherRule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                ?case (om:hasRetractionRule | ^om:retractionRuleOf) ?rule .
                ?rule (ord:precedes | ord:follows | ^ord:precedes | ^ord:follows) ?otherRule .
                FILTER NOT EXISTS {
                    $this (om:hasCase | ^om:caseOf) ?otherCase .
                    ?otherCase (om:hasRetractionRule | ^om:retractionRuleOf) ?otherRule .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "Every rule participating in a meaningful dependency must identify this migration set as its ord:inOrdering context." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            PREFIX sh: <http://www.w3.org/ns/shacl#>
            SELECT DISTINCT $this ?rule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                {
                    ?case sh:rule ?rule .
                }
                UNION
                {
                    ?case (om:hasRetractionRule | ^om:retractionRuleOf) ?rule .
                }
                ?rule (ord:precedes | ord:follows | ^ord:precedes | ^ord:follows) ?otherRule .
                FILTER NOT EXISTS { ?rule ord:inOrdering $this . }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "The partial order stated with ord:precedes and ord:follows for a migration set must be acyclic." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            PREFIX sh: <http://www.w3.org/ns/shacl#>
            SELECT DISTINCT $this ?rule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                {
                    ?case sh:rule ?rule .
                }
                UNION
                {
                    ?case (om:hasRetractionRule | ^om:retractionRuleOf) ?rule .
                }
                ?rule (ord:precedes | ^ord:follows)+ ?rule .
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "When sh:order values encode an ord:precedes or ord:follows dependency, the earlier rule must have the lower numeric order." ;
        sh:select """
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX ord: <https://ontology.inferal.com/modules/ordering/>
            PREFIX sh: <http://www.w3.org/ns/shacl#>
            SELECT DISTINCT $this ?earlierRule ?laterRule WHERE {
                $this (om:hasCase | ^om:caseOf) ?case .
                {
                    ?case sh:rule ?earlierRule .
                }
                UNION
                {
                    ?case (om:hasRetractionRule | ^om:retractionRuleOf) ?earlierRule .
                }
                {
                    ?earlierRule ord:precedes ?laterRule .
                }
                UNION
                {
                    ?laterRule ord:follows ?earlierRule .
                }
                ?earlierRule sh:order ?earlierOrder .
                ?laterRule sh:order ?laterOrder .
                FILTER (?earlierOrder >= ?laterOrder)
            }
        """ ;
    ] .

:MigrationVerificationCaseShape
    a sh:NodeShape ;
    rdfs:label "migration verification case shape" ;
    rdfs:comment "Validates an atomic migration verification criterion that pairs distinct source-side and target-side W3C query-evaluation tests with one common expected result graph. A test states its query and its expected result; qt:data is optional because the executing harness supplies each graph, the stored data to the source test and the graph the rules produced to the target test, and the produced graph exists only for the duration of the run." ;
    sh:targetClass :MigrationVerificationCase ;
    sh:nodeKind sh:IRI ;
    sh:property [
        sh:path rdfs:label ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path rdfs:comment ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path :verificationOf ;
        sh:class :OntologyMigrationSet ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :sourceTest ;
        sh:class mf:QueryEvaluationTest ;
        sh:nodeKind sh:IRI ;
        sh:node [
            sh:property [
                sh:path mf:action ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
                sh:node [
                    sh:property [
                        sh:path qt:query ;
                        sh:nodeKind sh:IRI ;
                        sh:minCount 1 ;
                        sh:maxCount 1 ;
                    ] ;
                    sh:property [
                        sh:path qt:data ;
                        sh:nodeKind sh:IRI ;
                        sh:maxCount 1 ;
                    ] ;
                ] ;
            ] ;
            sh:property [
                sh:path mf:result ;
                sh:nodeKind sh:IRI ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
            ] ;
        ] ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :targetTest ;
        sh:class mf:QueryEvaluationTest ;
        sh:nodeKind sh:IRI ;
        sh:node [
            sh:property [
                sh:path mf:action ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
                sh:node [
                    sh:property [
                        sh:path qt:query ;
                        sh:nodeKind sh:IRI ;
                        sh:minCount 1 ;
                        sh:maxCount 1 ;
                    ] ;
                    sh:property [
                        sh:path qt:data ;
                        sh:nodeKind sh:IRI ;
                        sh:maxCount 1 ;
                    ] ;
                ] ;
            ] ;
            sh:property [
                sh:path mf:result ;
                sh:nodeKind sh:IRI ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
            ] ;
        ] ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:disjoint :sourceTest ;
    ] ;
    sh:sparql [
        sh:message "Paired source and target tests must each declare one query, one data graph, and the same expected result through the W3C test manifest vocabularies." ;
        sh:select """
            PREFIX mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>
            PREFIX om: <https://ontology.inferal.com/modules/ontology-migration/>
            PREFIX qt: <http://www.w3.org/2001/sw/DataAccess/tests/test-query#>
            SELECT $this ?sourceTest ?targetTest WHERE {
                $this om:sourceTest ?sourceTest ;
                      om:targetTest ?targetTest .
                FILTER NOT EXISTS {
                    ?sourceTest mf:result ?expectedResult .
                    ?targetTest mf:result ?expectedResult .
                }
            }
        """ ;
    ] .

:TransformationCaseShape
    a sh:NodeShape ;
    rdfs:label "transformation case shape" ;
    rdfs:comment "Validates each node shape explicitly associated with a migration set through has case or case of as a named, documented transformation case with at least one explicit SHACL target and at least one supported rule." ;
    sh:targetObjectsOf :hasCase ;
    sh:targetSubjectsOf :caseOf ;
    sh:nodeKind sh:IRI ;
    sh:property [
        sh:path rdf:type ;
        sh:hasValue sh:NodeShape ;
    ] ;
    sh:property [
        sh:path rdfs:label ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path rdfs:comment ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path sh:rule ;
        sh:node :TransformationRuleShape ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path [
            sh:alternativePath (
                :hasRetractionRule
                [ sh:inversePath :retractionRuleOf ]
            )
        ] ;
        sh:node :RetractionRuleShape ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:property [
        sh:path :caseOf ;
        sh:class :OntologyMigrationSet ;
        sh:nodeKind sh:IRI ;
    ] ;
    sh:or (
        [ sh:property [ sh:path sh:targetClass ; sh:minCount 1 ] ]
        [ sh:property [ sh:path sh:targetSubjectsOf ; sh:minCount 1 ] ]
        [ sh:property [ sh:path sh:targetObjectsOf ; sh:minCount 1 ] ]
        [ sh:property [ sh:path sh:targetNode ; sh:minCount 1 ] ]
        [ sh:property [ sh:path sh:target ; sh:minCount 1 ] ]
    ) .

:TransformationRuleShape
    a sh:NodeShape ;
    rdfs:label "transformation rule shape" ;
    rdfs:comment "Validates the minimum SHACL-AF syntax needed for a Triple Rule or SPARQL Rule reached from an explicitly associated ontology transformation case. Only rules reached from selected cases enter this contract. Meaningful dependencies use ord:precedes or ord:follows; sh:order alone is not a sufficient dependency declaration." ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:or (
        [
            sh:property [
                sh:path rdf:type ;
                sh:hasValue sh:TripleRule ;
            ] ;
            sh:property [ sh:path sh:subject ; sh:minCount 1 ; sh:maxCount 1 ] ;
            sh:property [ sh:path sh:predicate ; sh:minCount 1 ; sh:maxCount 1 ] ;
            sh:property [ sh:path sh:object ; sh:minCount 1 ; sh:maxCount 1 ]
        ]
        [
            sh:property [
                sh:path rdf:type ;
                sh:hasValue sh:SPARQLRule ;
            ] ;
            sh:property [
                sh:path sh:construct ;
                sh:datatype xsd:string ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
            ]
        ]
    ) ;
    sh:property [
        sh:path sh:order ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path ord:inOrdering ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path sh:deactivated ;
        sh:datatype xsd:boolean ;
        sh:maxCount 1 ;
    ] .

:RetractionRuleShape
    a sh:NodeShape ;
    rdfs:label "retraction rule shape" ;
    rdfs:comment "Validates every named SHACL SPARQL Rule explicitly associated with a migration case through has retraction rule or retraction rule of. The rule constructs an exact post-validation source-retraction plan and is not attached through sh:rule during additive execution. Meaningful dependencies use ord:precedes or ord:follows; sh:order alone is not a sufficient dependency declaration." ;
    sh:targetObjectsOf :hasRetractionRule ;
    sh:targetSubjectsOf :retractionRuleOf ;
    sh:nodeKind sh:IRI ;
    sh:property [
        sh:path rdf:type ;
        sh:hasValue sh:SPARQLRule ;
    ] ;
    sh:property [
        sh:path rdfs:label ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path rdfs:comment ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
    ] ;
    sh:property [
        sh:path sh:construct ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path sh:order ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path ord:inOrdering ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path sh:deactivated ;
        sh:datatype xsd:boolean ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :retractionRuleOf ;
        sh:class sh:NodeShape ;
        sh:nodeKind sh:IRI ;
    ] .
