@prefix vho: <https://vho.bio/ns/core#>.
@prefix shape: <https://vho.bio/shapes/>.
@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 skos: <http://www.w3.org/2004/02/skos/core#>.
@prefix dcterms: <http://purl.org/dc/terms/>.
@prefix sh: <http://www.w3.org/ns/shacl#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
@prefix prov: <http://www.w3.org/ns/prov#>.
@prefix obo: <http://purl.obolibrary.org/obo/>.

<https://vho.bio/ns/core/shapes> dcterms:creator <https://vho.bio/contributor/crittercad>;
    dcterms:description "SHACL library for the ontology's own integrity (capacity and role well-formedness) and for forward-chained derivation rules (substrate composition, conditional trait materialisation). Whether a given vivarium configuration satisfies a species' requirements is a question for consuming applications, which read the ontology's assertions and evaluate designs under their own published criteria.";
    dcterms:license <https://creativecommons.org/licenses/by/4.0/>;
    dcterms:title "VHO Core Shapes";
    a owl:Ontology;
    owl:imports <https://vho.bio/ns/core>, <https://vho.bio/ns/core/patterns>, <https://vho.bio/ns/core/properties>;
    owl:versionIRI <https://vho.bio/ns/2026-08-19/core/shapes>;
    owl:versionInfo "0.2.0".
shape:CapacityRealizedWhenShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Every capacity declares its realization condition"@en;
    sh:property _:n3-3;
    sh:targetClass vho:Capacity.
shape:CareAxisCoverageShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Report uncurated applicable care axes"@en;
    sh:severity sh:Info;
    sh:sparql _:n3-13;
    sh:targetSubjectsOf vho:hasCareRegime.
shape:EndorsementShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Every endorsement binds subject, record, agent, and date"@en;
    sh:property _:n3-16, _:n3-17, _:n3-18, _:n3-19, _:n3-20;
    sh:targetClass vho:Endorsement.
shape:PostMoultThinIntegumentRule dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:comment "Trait-from-period materialisation: a conditional trait is added to the organism when the design's currentPeriod matches a marker period, complementing the capacity-and-trait model."@en;
    rdfs:label "Materialise ThinIntegument on theraphosids during PostMoult"@en;
    sh:rule _:n3-15;
    sh:targetClass vho:Theraphosidae.
shape:RetractionAntecedentShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Every retraction has an inherited antecedent"@en;
    sh:sparql _:n3-11;
    sh:targetSubjectsOf vho:inapplicableAxis, vho:lacksCapacity, vho:lacksQuality, vho:lacksTrait, vho:waivesRole.
shape:RetractionConflictShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "No class both asserts and retracts one member"@en;
    sh:sparql _:n3-10;
    sh:targetSubjectsOf vho:inapplicableAxis, vho:lacksCapacity, vho:lacksQuality, vho:lacksTrait, vho:waivesRole.
shape:RoleBindingShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Every role binds context and holder"@en;
    sh:property _:n3-4, _:n3-5, _:n3-6;
    sh:targetClass vho:Role.
shape:RoleRequirementShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Every role requirement is well-formed and non-duplicative"@en;
    sh:property _:n3-7, _:n3-8;
    sh:sparql _:n3-9;
    sh:targetClass vho:RoleRequirement.
shape:SubstrateWaterRetentionRule dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "Derive substrate water retention from component mass fractions"@en;
    sh:rule _:n3-14;
    sh:targetClass vho:Substrate.
shape:WaiverReifiedConflictShape dcterms:contributor <https://vho.bio/contributor/crittercad>;
    a sh:NodeShape;
    rdfs:label "No class both requires a role through a requirement node and waives it"@en;
    sh:sparql _:n3-12;
    sh:targetClass vho:RoleRequirement.
_:n3-10 a sh:SPARQLConstraint;
    sh:message "A class must not both assert and retract the same member.";
    sh:prefixes <https://vho.bio/ns/core/shapes>;
    sh:select "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            SELECT $this ?value WHERE {\n              ?retract vho:retracts ?assert .\n              $this ?retract ?value .\n              $this ?assert ?value .\n            }\n        ".
_:n3-11 a sh:SPARQLConstraint;
    sh:message "A retraction is well-formed only when an ancestor class asserts the retracted member.";
    sh:prefixes <https://vho.bio/ns/core/shapes>;
    sh:select "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n            SELECT $this ?value WHERE {\n              ?retract vho:retracts ?assert .\n              $this ?retract ?value .\n              FILTER NOT EXISTS {\n                $this rdfs:subClassOf+ ?anc .\n                ?anc ?assert ?value .\n              }\n              FILTER NOT EXISTS {\n                $this rdfs:subClassOf+ ?anc2 .\n                ?anc2 vho:roleRequirement ?rr .\n                ?rr vho:forRole ?value .\n                FILTER(?retract = vho:waivesRole)\n              }\n            }\n        ".
_:n3-12 a sh:SPARQLConstraint;
    sh:message "A class must not both require a role through a vho:RoleRequirement and waive it.";
    sh:prefixes <https://vho.bio/ns/core/shapes>;
    sh:select "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            SELECT $this ?value WHERE {\n              ?class vho:roleRequirement $this .\n              $this vho:forRole ?value .\n              ?class vho:waivesRole ?value .\n            }\n        ".
_:n3-13 a sh:SPARQLConstraint;
    sh:message "Applicable care axis not yet curated for this class.";
    sh:prefixes <https://vho.bio/ns/core/shapes>;
    sh:select "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n            SELECT DISTINCT $this ?value WHERE {\n              $this rdfs:subClassOf* ?anc .\n              ?anc vho:applicableAxis ?value .\n              FILTER NOT EXISTS {\n                $this rdfs:subClassOf* ?r .\n                ?r vho:inapplicableAxis ?value .\n              }\n              FILTER NOT EXISTS {\n                ?value vho:coversProperty ?p .\n                $this rdfs:subClassOf* ?c .\n                ?c ?p ?direct .\n              }\n              FILTER NOT EXISTS {\n                ?value vho:coversProperty ?p2 .\n                $this rdfs:subClassOf* ?cr .\n                ?cr vho:hasCareRegime ?reg .\n                ?reg ?p2 ?viaRegime .\n              }\n              FILTER NOT EXISTS {\n                $this rdfs:subClassOf* ?c2 .\n                ?c2 vho:assertsNoRequirement ?nr .\n                ?nr vho:onAxis ?value .\n              }\n            }\n        ".
_:n3-14 a sh:SPARQLRule;
    sh:construct "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            CONSTRUCT {\n              $this vho:derivedWaterRetention ?total .\n            } WHERE {\n              SELECT $this (SUM(?contribution) AS ?total) WHERE {\n                $this vho:hasComponent ?c .\n                ?c vho:componentType ?type ;\n                   vho:massFraction ?fraction .\n                ?type vho:waterRetention ?value .\n                BIND(?value * ?fraction AS ?contribution)\n              } GROUP BY $this\n            }\n        ";
    sh:prefixes <https://vho.bio/ns/core/shapes>.
_:n3-15 a sh:SPARQLRule;
    sh:construct "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            CONSTRUCT {\n              $this vho:hasTrait vho:ThinIntegument .\n            } WHERE {\n              SELECT $this WHERE {\n                ?design vho:hasOrganism $this ;\n                        vho:currentPeriod vho:PostMoult .\n              }\n            }\n        ";
    sh:prefixes <https://vho.bio/ns/core/shapes>.
_:n3-16 sh:maxCount 1;
    sh:message "Every vho:Endorsement must name exactly one vho:endorsedSubject.";
    sh:minCount 1;
    sh:path vho:endorsedSubject.
_:n3-17 sh:maxCount 1;
    sh:message "Every vho:Endorsement must name exactly one vho:endorsedRecord.";
    sh:minCount 1;
    sh:path vho:endorsedRecord.
_:n3-18 sh:message "Every vho:Endorsement must name at least one reviewing agent via prov:wasAssociatedWith.";
    sh:minCount 1;
    sh:path prov:wasAssociatedWith.
_:n3-19 sh:maxCount 1;
    sh:message "Every vho:Endorsement must carry exactly one prov:endedAtTime.";
    sh:minCount 1;
    sh:path prov:endedAtTime.
_:n3-20 sh:class vho:CareAxis;
    sh:message "Every vho:reviewedAxis value must be a vho:CareAxis.";
    sh:path vho:reviewedAxis.
_:n3-3 sh:class vho:Trait;
    sh:message "Every vho:Capacity must declare at least one vho:realizedWhen pointing to a vho:Trait.";
    sh:minCount 1;
    sh:path vho:realizedWhen.
_:n3-4 sh:class vho:Design;
    sh:message "Every role instance must declare a vho:inContext binding it to a vho:Design.";
    sh:minCount 1;
    sh:path vho:inContext.
_:n3-5 sh:message "Every role instance must declare a vho:roleHolderFor identifying the beneficiary.";
    sh:minCount 1;
    sh:path vho:roleHolderFor.
_:n3-6 sh:message "Every role instance must declare a vho:playedBy identifying the continuant playing the role.";
    sh:minCount 1;
    sh:path vho:playedBy.
_:n3-7 sh:maxCount 1;
    sh:message "Every vho:RoleRequirement must name exactly one vho:forRole.";
    sh:minCount 1;
    sh:path vho:forRole.
_:n3-8 sh:maxCount 1;
    sh:message "vho:minRoleCount, when present, must be a single value of at least 1.";
    sh:minInclusive 1;
    sh:path vho:minRoleCount.
_:n3-9 a sh:SPARQLConstraint;
    sh:message "A class states a role in one form only: this vho:RoleRequirement duplicates a bare vho:requiresRole assertion of the same role on the same class.";
    sh:prefixes <https://vho.bio/ns/core/shapes>;
    sh:select "\n            PREFIX vho: <https://vho.bio/ns/core#>\n            SELECT $this WHERE {\n              ?class vho:roleRequirement $this .\n              $this vho:forRole ?role .\n              ?class vho:requiresRole ?role .\n            }\n        ".
