Semantic Flow Core SHACL Shapes

a owl:Ontology

SHACL shapes for validating Semantic Flow data structures and operational invariants.

Children
Properties
Latest historical manifestation file
ontology/shacl/releases/v0.2.0/ttl/semantic-flow-core-shacl.ttl Raw file
@base <https://semantic-flow.github.io/sflo/ontology/shacl/> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dcterms: <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 schema: <https://schema.org/> .
@prefix sfcfg: <https://semantic-flow.github.io/sflo/config/> .
@prefix sflo: <https://semantic-flow.github.io/sflo/ontology/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix vann: <http://purl.org/vocab/vann/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://semantic-flow.github.io/sflo/ontology/shacl>
  dcterms:title "Semantic Flow Core SHACL Shapes";
  rdf:type owl:Ontology;
  dcterms:description "SHACL shapes for validating Semantic Flow data structures and operational invariants.";
  dcterms:creator <https://djradon.github.io/ns/dave-richardson> ;
  dcterms:hasVersion <https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0>;
  dcterms:modified "2026-05-19"^^xsd:date;
  vann:preferredNamespacePrefix "sflo-shacl";
  vann:preferredNamespaceUri "https://semantic-flow.github.io/sflo/ontology/shacl/";
  owl:versionIRI <https://raw.githubusercontent.com/semantic-flow/sflo/refs/tags/v0.2.0/semantic-flow-core-shacl.ttl>;
  owl:versionInfo "0.2.0" .

<https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0>
  rdf:type owl:Ontology, sflo:HistoricalState;
  dcterms:isVersionOf <https://semantic-flow.github.io/sflo/ontology/shacl>;
  dcterms:issued "2026-05-24"^^xsd:date;
  owl:versionInfo "0.2.0";
  schema:contentUrl <https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0/ttl/semantic-flow-core-shacl.ttl>;
  sflo:hasManifestation <https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0/ttl> .

<https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0/ttl>
  dcat:downloadURL <https://semantic-flow.github.io/sflo/ontology/shacl/releases/v0.2.0/ttl/semantic-flow-core-shacl.ttl> .

<https://semantic-flow.github.io/sflo/ontology/shacl/ReferenceLinkShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A ReferenceLink must identify the thing it is about and the RDF reference source it uses.";
  rdfs:label "ReferenceLink basics";
  sh:property [
    sh:class sflo:ReferenceSource;
    sh:maxCount 1;
    sh:message "A ReferenceLink MUST have exactly one sflo:hasReferenceSource value typed sflo:ReferenceSource.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasReferenceSource;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "A ReferenceLink SHOULD have at least one sflo:hasReferenceRole.";
    sh:minCount 1;
    sh:path sflo:hasReferenceRole;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:maxCount 1;
    sh:message "A ReferenceLink MUST have exactly one sflo:referenceLinkFor.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:referenceLinkFor;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ReferenceLink .

<https://semantic-flow.github.io/sflo/ontology/shacl/ReferenceSourceShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A ReferenceSource identifies RDF data used by a ReferenceLink.";
  rdfs:label "ReferenceSource basics";
  sh:property [
    sh:class sflo:RdfDocument;
    sh:maxCount 1;
    sh:message "If present, a ReferenceSource sflo:hasTargetArtifact value MUST be an IRI typed sflo:RdfDocument.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasTargetArtifact;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ReferenceSource .

<https://semantic-flow.github.io/sflo/ontology/shacl/ReferenceLinkForNotKnopShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Reference links must be about the referent or mesh subject, not about the Knop support object itself.";
  rdfs:label "ReferenceLink subject must not be a Knop";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "sflo:referenceLinkFor MUST identify the actual subject resource, not a sflo:Knop support object.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:referenceLinkFor ?target .\n  ?target a sflo:Knop .\n}";
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ReferenceLink .

<https://semantic-flow.github.io/sflo/ontology/shacl/ReferenceCatalogOwnerShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Only Knops and SemanticMeshes may own a ReferenceCatalog in this version of the model.";
  rdfs:label "ReferenceCatalog owner constraints";
  sh:property [
    sh:class sflo:ReferenceCatalog;
    sh:maxCount 1;
    sh:message "An owner may have at most one sflo:hasReferenceCatalog value, and it must be an IRI typed sflo:ReferenceCatalog.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasReferenceCatalog;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "Only sflo:Knop or sflo:SemanticMesh resources may own a sflo:hasReferenceCatalog in this version of the model.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT DISTINCT $this WHERE {\n  $this sflo:hasReferenceCatalog ?catalog .\n  FILTER NOT EXISTS { $this a sflo:Knop }\n  FILTER NOT EXISTS { $this a sflo:SemanticMesh }\n}";
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:hasReferenceCatalog .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopSourceRegistryShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A KnopSourceRegistry catalogs source bindings and extraction provenance for one Knop.";
  rdfs:label "KnopSourceRegistry";
  sh:property [
    sh:class sflo:ArtifactResolutionTarget;
    sh:message "A KnopSourceRegistry MUST have at least one sflo:hasSourceBinding value typed sflo:ArtifactResolutionTarget.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasSourceBinding;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:KnopSourceRegistry .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopSourceRegistryAttachmentShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Only Knops may own a KnopSourceRegistry in this version of the model.";
  rdfs:label "KnopSourceRegistry owner constraints";
  sh:property [
    sh:class sflo:KnopSourceRegistry;
    sh:maxCount 1;
    sh:message "A Knop may have at most one sflo:hasKnopSourceRegistry value, and it must be an IRI typed sflo:KnopSourceRegistry.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasKnopSourceRegistry;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "Only sflo:Knop resources may own a sflo:hasKnopSourceRegistry in this version of the model.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT DISTINCT $this WHERE {\n  $this sflo:hasKnopSourceRegistry ?registry .\n  FILTER NOT EXISTS { $this a sflo:Knop }\n}";
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:hasKnopSourceRegistry .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopSourceBindingShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A repository-backed source binding carried by a KnopSourceRegistry records the target artifact and its repository source locator. Exact replay evidence such as a commit or digest is strongly recommended, but working/ref-following bindings are valid when operational policy allows them.";
  rdfs:label "Repository-backed Knop source binding";
  sh:property [
    sh:maxCount 1;
    sh:message "A repository-backed Knop source binding MUST identify exactly one sflo:hasTargetArtifact IRI.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasTargetArtifact;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A repository-backed Knop source binding MUST declare exactly one sflo:targetLocalRelativePath string literal.";
    sh:minCount 1;
    sh:path sflo:targetLocalRelativePath;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A repository-backed Knop source binding SHOULD declare sflo:expectsContentDigest when deterministic byte replay matters.";
    sh:path sflo:expectsContentDigest;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:RepositorySourceLocator;
    sh:maxCount 1;
    sh:message "A repository-backed Knop source binding MUST identify exactly one sflo:hasTargetRepositorySource value typed sflo:RepositorySourceLocator.";
    sh:minCount 1;
    sh:nodeKind sh:BlankNodeOrIRI;
    sh:path sflo:hasTargetRepositorySource;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sflo:ArtifactResolutionMode;
    sh:maxCount 1;
    sh:message "A repository-backed Knop source binding SHOULD declare one sflo:hasArtifactResolutionMode value typed sflo:ArtifactResolutionMode.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasArtifactResolutionMode;
    sh:severity sh:Warning
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "A repository-backed source binding SHOULD include sourceRepositoryCommit, expectsContentDigest, or hasContentDigest evidence when deterministic replay matters.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:hasTargetRepositorySource ?locator .\n  FILTER NOT EXISTS { ?locator sflo:sourceRepositoryCommit ?commit }\n  FILTER NOT EXISTS { $this sflo:expectsContentDigest ?digest }\n  FILTER NOT EXISTS { ?locator sflo:hasContentDigest ?locatorDigest }\n}";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:hasTargetRepositorySource .

<https://semantic-flow.github.io/sflo/ontology/shacl/LocalWorkingSourceBindingShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A local working source binding uses targetLocalRelativePath directly, without a repository source locator. It is mutable working-byte resolution unless exact state, located-file, or digest evidence says otherwise.";
  rdfs:label "Local working source binding";
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A local working source binding MUST declare at most one sflo:targetLocalRelativePath string literal.";
    sh:path sflo:targetLocalRelativePath;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "A local working source binding SHOULD declare sflo:hasArtifactResolutionMode, normally sflo:artifactResolutionMode_working, unless exact target coordinates make the mode obvious.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:targetLocalRelativePath ?path .\n  FILTER NOT EXISTS { $this sflo:hasTargetRepositorySource ?repo }\n  FILTER NOT EXISTS { $this sflo:hasArtifactResolutionMode ?mode }\n  FILTER NOT EXISTS { $this sflo:hasRequestedTargetState ?state }\n  FILTER NOT EXISTS { $this sflo:hasTargetLocatedFile ?file }\n  FILTER NOT EXISTS { $this sflo:expectsContentDigest ?digest }\n}";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:targetLocalRelativePath .

<https://semantic-flow.github.io/sflo/ontology/shacl/RepositorySourceLocatorShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A repository source locator records durable repository coordinates for bytes or a tree path without implying local checkout access.";
  rdfs:label "RepositorySourceLocator";
  sh:property [
    sh:maxCount 1;
    sh:message "A RepositorySourceLocator MUST declare exactly one sflo:sourceRepositoryUrl literal.";
    sh:minCount 1;
    sh:nodeKind sh:Literal;
    sh:path sflo:sourceRepositoryUrl;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A RepositorySourceLocator MUST declare exactly one sflo:sourceRepositoryRef string literal.";
    sh:minCount 1;
    sh:path sflo:sourceRepositoryRef;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "If present, sflo:sourceRepositoryCommit MUST be a single string literal.";
    sh:path sflo:sourceRepositoryCommit;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A RepositorySourceLocator MUST declare exactly one sflo:sourceRepositoryPath string literal.";
    sh:minCount 1;
    sh:path sflo:sourceRepositoryPath;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:RepositorySourceLocator .

<https://semantic-flow.github.io/sflo/ontology/shacl/RepositorySourceFloatingLocatorShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A floating repository source locator records durable repository identity and a path from the repository root for mutable working bytes. It deliberately omits ref, commit, digest, and local checkout path evidence.";
  rdfs:label "RepositorySourceFloatingLocator";
  sh:property [
    sh:maxCount 1;
    sh:message "A RepositorySourceFloatingLocator MUST declare exactly one sflo:sourceRepositoryUrl literal.";
    sh:minCount 1;
    sh:nodeKind sh:Literal;
    sh:path sflo:sourceRepositoryUrl;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A RepositorySourceFloatingLocator MUST declare exactly one sflo:sourceRepositoryPathFromRoot string literal.";
    sh:minCount 1;
    sh:path sflo:sourceRepositoryPathFromRoot;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A RepositorySourceFloatingLocator MUST NOT declare sflo:sourceRepositoryRef; floating working sources use the currently checked-out ref from operational resolution.";
    sh:path sflo:sourceRepositoryRef;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A RepositorySourceFloatingLocator MUST NOT declare sflo:sourceRepositoryCommit; commit-pinned source resolution should use RepositorySourceLocator.";
    sh:path sflo:sourceRepositoryCommit;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A RepositorySourceFloatingLocator MUST NOT declare sflo:hasContentDigest; digest-pinned source resolution should use exact repository source evidence.";
    sh:path sflo:hasContentDigest;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:RepositorySourceFloatingLocator .

<https://semantic-flow.github.io/sflo/ontology/shacl/FloatingRepositorySourceBindingShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A branch-based mesh floating source binding resolves mutable working bytes from a repository checkout selected by operational policy. It should not persist host-local source paths.";
  rdfs:label "Floating repository source binding";
  sh:property [
    sh:class sflo:RepositorySourceFloatingLocator;
    sh:maxCount 1;
    sh:message "A floating repository source binding MUST identify exactly one sflo:hasRepositorySourceFloatingLocator value typed sflo:RepositorySourceFloatingLocator.";
    sh:minCount 1;
    sh:nodeKind sh:BlankNodeOrIRI;
    sh:path sflo:hasRepositorySourceFloatingLocator;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A floating repository source binding MUST NOT declare sflo:targetLocalRelativePath.";
    sh:path sflo:targetLocalRelativePath;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A floating repository source binding MUST NOT declare sflo:workingLocalRelativePath.";
    sh:path sflo:workingLocalRelativePath;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:maxCount 0;
    sh:message "A floating repository source binding MUST NOT declare sflo:hasWorkingLocatedFile.";
    sh:path sflo:hasWorkingLocatedFile;
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:hasRepositorySourceFloatingLocator .

<https://semantic-flow.github.io/sflo/ontology/shacl/PreviousHistoricalStateShape>
  rdf:type sh:NodeShape;
  rdfs:comment "previousHistoricalState is optional but, if present, must point to exactly one HistoricalState (the immediately preceding published state).";
  rdfs:label "HistoricalState revision linkage";
  sh:property [
    sh:class sflo:HistoricalState;
    sh:maxCount 1;
    sh:message "sflo:previousHistoricalState must have at most one value, and it must be an IRI typed sflo:HistoricalState.";
    sh:nodeKind sh:IRI;
    sh:path sflo:previousHistoricalState;
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:previousHistoricalState .

<https://semantic-flow.github.io/sflo/ontology/shacl/PreviousHistoricalStateNotSelfShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Disallow self-links in the published revision chain.";
  rdfs:label "previousHistoricalState must not be self";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "A HistoricalState MUST NOT have itself as sflo:previousHistoricalState.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:previousHistoricalState $this .\n}";
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:previousHistoricalState .

<https://semantic-flow.github.io/sflo/ontology/shacl/PreferredPayloadFileSlugShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Validates sflo:preferredPayloadFileSlug token constraints (filename stem preference).";
  rdfs:label "preferredPayloadFileSlug";
  sh:property [
    sh:message "If present, sflo:preferredPayloadFileSlug SHOULD be a portable filename stem token (ASCII letters/digits with . _ -).";
    sh:path sflo:preferredPayloadFileSlug;
    sh:pattern "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$";
    sh:severity sh:Warning
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "If present, sflo:preferredPayloadFileSlug MUST be a single xsd:string.";
    sh:path sflo:preferredPayloadFileSlug;
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:PayloadArtifact .

<https://semantic-flow.github.io/sflo/ontology/shacl/PreferredPayloadFileSlugIndexBanShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Best-effort guardrail to prevent generating index.html unintentionally.";
  rdfs:label "preferredPayloadFileSlug should avoid index";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "sflo:preferredPayloadFileSlug SHOULD NOT be 'index' or 'index.html' (index.html is reserved for ResourcePage files).";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:preferredPayloadFileSlug ?s .\n  BIND(LCASE(STR(?s)) AS ?lc)\n  FILTER(?lc = \"index\" || ?lc = \"index.html\")\n}";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:preferredPayloadFileSlug .

<https://semantic-flow.github.io/sflo/ontology/shacl/MeshSlotsShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Operational slot expectations for common SemanticMesh-associated artifacts.";
  rdfs:label "SemanticMesh slots";
  sh:property [
    sh:class sflo:ReferenceCatalog;
    sh:maxCount 1;
    sh:message "If present, a SemanticMesh may have at most one sflo:hasReferenceCatalog, and it must be an IRI typed sflo:ReferenceCatalog.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasReferenceCatalog;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:LocatedFile;
    sh:maxCount 1;
    sh:message "If present, a SemanticMesh may have at most one sflo:hasWorkingMeshInventoryFile, and it must be an IRI typed sflo:LocatedFile.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasWorkingMeshInventoryFile;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:message "A SemanticMesh MUST have exactly one sflo:hasMeshMetadata value typed sflo:MeshMetadata.";
    sh:path sflo:hasMeshMetadata;
    sh:qualifiedMaxCount 1;
    sh:qualifiedMinCount 1;
    sh:qualifiedValueShape [ sh:class sflo:MeshMetadata ];
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "A SemanticMesh MUST have exactly one sflo:hasMeshInventory value typed sflo:MeshInventory.";
    sh:path sflo:hasMeshInventory;
    sh:qualifiedMaxCount 1;
    sh:qualifiedMinCount 1;
    sh:qualifiedValueShape [ sh:class sflo:MeshInventory ];
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:SemanticMesh .

<https://semantic-flow.github.io/sflo/ontology/shacl/MeshBaseShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Ensures a SemanticMesh typically declares a canonical meshBase URI literal.";
  rdfs:label "SemanticMesh base sanity";
  sh:property [
    sh:datatype xsd:anyURI;
    sh:maxCount 1;
    sh:message "A SemanticMesh SHOULD declare exactly one sflo:meshBase (xsd:anyURI).";
    sh:minCount 1;
    sh:path sflo:meshBase;
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:SemanticMesh .

<https://semantic-flow.github.io/sflo/ontology/shacl/LocatedFileForStateConsistencyShape>
  rdf:type sh:NodeShape;
  rdfs:comment "If locatedFileForState is used as a shortcut, it should align with hasManifestation/locatedFileForManifestation.";
  rdfs:label "locatedFileForState consistency";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "If a HistoricalState uses sflo:locatedFileForState ?lf, there SHOULD exist an ArtifactManifestation ?m such that ($this sflo:hasManifestation ?m) and (?m sflo:locatedFileForManifestation ?lf).";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:locatedFileForState ?lf .\n  FILTER NOT EXISTS {\n    $this sflo:hasManifestation ?m .\n    ?m sflo:locatedFileForManifestation ?lf .\n  }\n}";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:locatedFileForState .

<https://semantic-flow.github.io/sflo/ontology/shacl/ContentDigestLiteralShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Content digests are compact algorithm-qualified strings such as sha256:<hex>. This shape checks the literal form only; byte retrieval, canonicalization, and cryptographic verification are runtime responsibilities.";
  rdfs:label "content digest literal";
  sh:property [
    sh:datatype xsd:string;
    sh:message "sflo:hasContentDigest values MUST be xsd:string literals.";
    sh:path sflo:hasContentDigest;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "sflo:hasContentDigest values SHOULD include an algorithm prefix followed by ':', for example sha256:<hex>.";
    sh:path sflo:hasContentDigest;
    sh:pattern "^[A-Za-z][A-Za-z0-9+._-]*:.+$";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:hasContentDigest .

<https://semantic-flow.github.io/sflo/ontology/shacl/ExpectedContentDigestShape>
  rdf:type sh:NodeShape;
  rdfs:comment "ArtifactResolutionTargets may carry a single expected digest used by runtime resolution to verify fetched or loaded target bytes before use.";
  rdfs:label "expected content digest";
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "sflo:expectsContentDigest values MUST be a single xsd:string literal.";
    sh:path sflo:expectsContentDigest;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "sflo:expectsContentDigest values SHOULD include an algorithm prefix followed by ':', for example sha256:<hex>.";
    sh:path sflo:expectsContentDigest;
    sh:pattern "^[A-Za-z][A-Za-z0-9+._-]*:.+$";
    sh:severity sh:Warning
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "sflo:expectsContentDigest SHOULD be used on resources typed sflo:ArtifactResolutionTarget or a subclass such as sflo:ExtractionSource, sflo:ReferenceSource, sflo:ImportSource, or sflo:IntegrationSource.";
    sh:select "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\nPREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:expectsContentDigest ?digest .\n  FILTER NOT EXISTS { $this a/rdfs:subClassOf* sflo:ArtifactResolutionTarget }\n}";
    sh:severity sh:Warning
  ];
  sh:targetSubjectsOf sflo:expectsContentDigest .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactResolutionObservationLinkShape>
  rdf:type sh:NodeShape;
  rdfs:comment "ArtifactResolutionTargets may link to explicit observation records when an operation intentionally records resolved evidence.";
  rdfs:label "ArtifactResolutionTarget observations";
  sh:property [
    sh:class sflo:ArtifactResolutionObservation;
    sh:message "sflo:hasResolutionObservation values MUST be IRIs typed sflo:ArtifactResolutionObservation.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasResolutionObservation;
    sh:severity sh:Violation
  ];
  sh:targetSubjectsOf sflo:hasResolutionObservation .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactResolutionObservationShape>
  rdf:type sh:NodeShape;
  rdfs:comment "An ArtifactResolutionObservation records evidence observed during one artifact-resolution event.";
  rdfs:label "ArtifactResolutionObservation";
  sh:property [
    sh:class sflo:HistoricalState;
    sh:maxCount 1;
    sh:message "If present, sflo:hasObservedTargetState MUST be a single IRI typed sflo:HistoricalState.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasObservedTargetState;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sflo:ArtifactManifestation;
    sh:maxCount 1;
    sh:message "If present, sflo:hasObservedTargetManifestation MUST be a single IRI typed sflo:ArtifactManifestation.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasObservedTargetManifestation;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sflo:LocatedFile;
    sh:maxCount 1;
    sh:message "If present, sflo:hasObservedTargetLocatedFile MUST be a single IRI typed sflo:LocatedFile.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasObservedTargetLocatedFile;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "If present, sflo:observedTargetLocalRelativePath MUST be a single local relative path literal.";
    sh:path sflo:observedTargetLocalRelativePath;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:string;
    sh:message "sflo:observedContentDigest values MUST be xsd:string literals.";
    sh:path sflo:observedContentDigest;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "sflo:observedContentDigest values SHOULD include an algorithm prefix followed by ':', for example sha256:<hex>.";
    sh:path sflo:observedContentDigest;
    sh:pattern "^[A-Za-z][A-Za-z0-9+._-]*:.+$";
    sh:severity sh:Warning
  ];
  sh:property [
    sh:datatype xsd:dateTime;
    sh:maxCount 1;
    sh:message "If present, sflo:observedAt MUST be a single xsd:dateTime literal.";
    sh:path sflo:observedAt;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class dcterms:Agent;
    sh:maxCount 1;
    sh:message "If present, sflo:observedBy MUST be a single IRI or blank node typed dcterms:Agent.";
    sh:nodeKind sh:BlankNodeOrIRI;
    sh:path sflo:observedBy;
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:ArtifactResolutionObservation .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactResolutionModeUsageShape>
  rdf:type sh:NodeShape;
  rdfs:comment "ArtifactResolutionMode usage checks distinguish mutable working-byte resolution from latest settled state resolution.";
  rdfs:label "ArtifactResolutionMode usage";
  sh:property [
    sh:class sflo:ArtifactResolutionMode;
    sh:maxCount 1;
    sh:message "If present, sflo:hasArtifactResolutionMode MUST be a single IRI typed sflo:ArtifactResolutionMode.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasArtifactResolutionMode;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "sflo:artifactResolutionMode_working SHOULD NOT be combined with an exact sflo:hasRequestedTargetState; exact state coordinates are settled-byte resolution.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:hasArtifactResolutionMode sflo:artifactResolutionMode_working ;\n        sflo:hasRequestedTargetState ?state .\n}";
    sh:severity sh:Warning
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "sflo:artifactResolutionMode_latestState SHOULD NOT be combined with an exact sflo:hasRequestedTargetState; the requested state is already exact.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:hasArtifactResolutionMode sflo:artifactResolutionMode_latestState ;\n        sflo:hasRequestedTargetState ?state .\n}";
    sh:severity sh:Info
  ];
  sh:targetSubjectsOf sflo:hasArtifactResolutionMode .

<https://semantic-flow.github.io/sflo/ontology/shacl/LatestHistoricalStateIsMemberShape>
  rdf:type sh:NodeShape;
  rdfs:comment "If latestHistoricalState is asserted, it must also be asserted as a hasHistoricalState of the same ArtifactHistory.";
  rdfs:label "latestHistoricalState must be among hasHistoricalState";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "If an ArtifactHistory has sflo:latestHistoricalState, that value MUST also appear among sflo:hasHistoricalState values.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:latestHistoricalState ?s .\n  FILTER NOT EXISTS { $this sflo:hasHistoricalState ?s }\n}";
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ArtifactHistory .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopSlotsShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Operational slot expectations for common Knop-associated artifacts.";
  rdfs:label "Knop slots";
  sh:property [
    sh:class sflo:KnopSourceRegistry;
    sh:maxCount 1;
    sh:message "If present, a Knop may have at most one sflo:hasKnopSourceRegistry, and it must be an IRI typed sflo:KnopSourceRegistry.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasKnopSourceRegistry;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:ExtractionSource;
    sh:maxCount 1;
    sh:message "If present, a Knop may have at most one sflo:hasExtractionSource value, and it must be an IRI typed sflo:ExtractionSource.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasExtractionSource;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:ReferenceCatalog;
    sh:maxCount 1;
    sh:message "If present, a Knop may have at most one sflo:hasReferenceCatalog, and it must be an IRI typed sflo:ReferenceCatalog.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasReferenceCatalog;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:LocatedFile;
    sh:maxCount 1;
    sh:message "If present, a Knop may have at most one sflo:hasWorkingKnopInventoryFile, and it must be an IRI typed sflo:LocatedFile.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasWorkingKnopInventoryFile;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:message "A Knop MUST have exactly one sflo:hasKnopInventory value typed sflo:KnopInventory.";
    sh:path sflo:hasKnopInventory;
    sh:qualifiedMaxCount 1;
    sh:qualifiedMinCount 1;
    sh:qualifiedValueShape [ sh:class sflo:KnopInventory ];
    sh:severity sh:Violation
  ];
  sh:property [
    sh:message "A Knop MUST have exactly one sflo:hasKnopMetadata value typed sflo:KnopMetadata.";
    sh:path sflo:hasKnopMetadata;
    sh:qualifiedMaxCount 1;
    sh:qualifiedMinCount 1;
    sh:qualifiedValueShape [ sh:class sflo:KnopMetadata ];
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:Knop .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopPayloadArtifactShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A Knop may have at most one primary payload resource typed sflo:PayloadArtifact.";
  rdfs:label "Knop payload artifact";
  sh:property [
    sh:message "A Knop may have at most one sflo:hasPayloadArtifact value typed sflo:PayloadArtifact.";
    sh:path sflo:hasPayloadArtifact;
    sh:qualifiedMaxCount 1;
    sh:qualifiedValueShape [ sh:class sflo:PayloadArtifact ];
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:Knop .

<https://semantic-flow.github.io/sflo/ontology/shacl/KnopDesignatorPathShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Each Knop must declare exactly one mesh-relative designatorPath.";
  rdfs:label "Knop designatorPath";
  sh:property [
    sh:datatype xsd:string;
    sh:maxCount 1;
    sh:message "A Knop MUST declare exactly one sflo:designatorPath (xsd:string).";
    sh:minCount 1;
    sh:path sflo:designatorPath;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:Knop .

<https://semantic-flow.github.io/sflo/ontology/shacl/HistoricalStateHasManifestationShape>
  rdf:type sh:NodeShape;
  rdfs:comment "HistoricalStates are content-bearing; they should usually have at least one ArtifactManifestation realization.";
  rdfs:label "HistoricalState realization presence";
  sh:property [
    sh:class sflo:ArtifactManifestation;
    sh:message "A HistoricalState SHOULD have at least one sflo:hasManifestation (IRI) typed sflo:ArtifactManifestation.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasManifestation;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:datatype xsd:nonNegativeInteger;
    sh:maxCount 1;
    sh:message "If present, sflo:stateOrdinal SHOULD be a single xsd:nonNegativeInteger.";
    sh:path sflo:stateOrdinal;
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:HistoricalState .

<https://semantic-flow.github.io/sflo/ontology/shacl/ExtractionSourceShape>
  rdf:type sh:NodeShape;
  rdfs:comment "An ExtractionSource identifies the RDF artifact state used to ground an extracted Knop-managed resource.";
  rdfs:label "ExtractionSource basics";
  sh:property [
    sh:class sflo:ArtifactResolutionMode;
    sh:maxCount 1;
    sh:message "If present, an ExtractionSource sflo:hasArtifactResolutionMode value MUST be typed sflo:ArtifactResolutionMode.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasArtifactResolutionMode;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "An ExtractionSource SHOULD declare sflo:hasArtifactResolutionMode unless exact target coordinates make the source state obvious.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this a sflo:ExtractionSource .\n  FILTER NOT EXISTS { $this sflo:hasArtifactResolutionMode ?mode }\n  FILTER NOT EXISTS { $this sflo:hasRequestedTargetState ?state }\n  FILTER NOT EXISTS { $this sflo:hasTargetLocatedFile ?file }\n  FILTER NOT EXISTS { $this sflo:expectsContentDigest ?digest }\n}";
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:HistoricalState;
    sh:maxCount 1;
    sh:message "If present, sflo:hasRequestedTargetState should be a single IRI typed sflo:HistoricalState.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasRequestedTargetState;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:RdfDocument;
    sh:maxCount 1;
    sh:message "An ExtractionSource MUST identify exactly one target artifact, and that artifact must be an IRI typed sflo:RdfDocument.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:hasTargetArtifact;
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ExtractionSource .

<https://semantic-flow.github.io/sflo/ontology/shacl/DigitalArtifactHistoryShape>
  rdf:type sh:NodeShape;
  rdfs:comment "DigitalArtifacts may point to explicit ArtifactHistory resources, may designate one current ArtifactHistory, may track the nextHistoryOrdinal for default history allocation, and may have at most one working LocatedFile.";
  rdfs:label "DigitalArtifact history and working surface";
  sh:property [
    sh:class sflo:LocatedFile;
    sh:maxCount 1;
    sh:message "A DigitalArtifact may have at most one sflo:hasWorkingLocatedFile, and it must be an IRI typed sflo:LocatedFile.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasWorkingLocatedFile;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:nonNegativeInteger;
    sh:maxCount 1;
    sh:message "If present, sflo:nextHistoryOrdinal SHOULD be a single xsd:nonNegativeInteger.";
    sh:path sflo:nextHistoryOrdinal;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:ArtifactHistory;
    sh:maxCount 1;
    sh:message "If present, sflo:currentArtifactHistory must be a single IRI typed sflo:ArtifactHistory.";
    sh:nodeKind sh:IRI;
    sh:path sflo:currentArtifactHistory;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sflo:ArtifactHistory;
    sh:message "If present, values of sflo:hasArtifactHistory must be IRIs typed sflo:ArtifactHistory.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasArtifactHistory;
    sh:severity sh:Info
  ];
  sh:property [
    sh:class sflo:LocatedFile;
    sh:message "If present, values of sflo:locatedFileForArtifact must be IRIs typed sflo:LocatedFile.";
    sh:nodeKind sh:IRI;
    sh:path sflo:locatedFileForArtifact;
    sh:severity sh:Info
  ];
  sh:targetClass sflo:DigitalArtifact .

<https://semantic-flow.github.io/sflo/ontology/shacl/CurrentArtifactHistoryIsMemberShape>
  rdf:type sh:NodeShape;
  rdfs:comment "If currentArtifactHistory is asserted, it must also be asserted as a hasArtifactHistory of the same DigitalArtifact.";
  rdfs:label "currentArtifactHistory must be among hasArtifactHistory";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "If a DigitalArtifact has sflo:currentArtifactHistory, that value MUST also appear among sflo:hasArtifactHistory values.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT $this WHERE {\n  $this sflo:currentArtifactHistory ?h .\n  FILTER NOT EXISTS { $this sflo:hasArtifactHistory ?h }\n}";
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:DigitalArtifact .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactManifestationLocatedFileShape>
  rdf:type sh:NodeShape;
  rdfs:comment "Many meshes expect ArtifactManifestations to have at least one LocatedFile. Warning because some manifestation identities may be non-locatable.";
  rdfs:label "ArtifactManifestation should be locatable (usually)";
  sh:property [
    sh:class sflo:LocatedFile;
    sh:message "An ArtifactManifestation SHOULD have at least one sflo:locatedFileForManifestation (IRI) typed sflo:LocatedFile.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sflo:locatedFileForManifestation;
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:ArtifactManifestation .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactHistoryShape>
  rdf:type sh:NodeShape;
  rdfs:comment "ArtifactHistory resources may point to published HistoricalStates, designate one latest HistoricalState, and carry history-local ordinal metadata.";
  rdfs:label "ArtifactHistory structure";
  sh:property [
    sh:datatype xsd:nonNegativeInteger;
    sh:maxCount 1;
    sh:message "If present, sflo:nextStateOrdinal SHOULD be a single xsd:nonNegativeInteger.";
    sh:path sflo:nextStateOrdinal;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:datatype xsd:nonNegativeInteger;
    sh:maxCount 1;
    sh:message "If present, sflo:historyOrdinal SHOULD be a single xsd:nonNegativeInteger.";
    sh:path sflo:historyOrdinal;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sflo:HistoricalState;
    sh:maxCount 1;
    sh:message "If present, sflo:latestHistoricalState must be a single IRI typed sflo:HistoricalState.";
    sh:nodeKind sh:IRI;
    sh:path sflo:latestHistoricalState;
    sh:severity sh:Info
  ];
  sh:property [
    sh:class sflo:HistoricalState;
    sh:message "If present, values of sflo:hasHistoricalState must be IRIs typed sflo:HistoricalState.";
    sh:nodeKind sh:IRI;
    sh:path sflo:hasHistoricalState;
    sh:severity sh:Info
  ];
  sh:targetClass sflo:ArtifactHistory .

<https://semantic-flow.github.io/sflo/ontology/shacl/ArtifactHasCurrentHistoryShape>
  rdf:type sh:NodeShape;
  rdfs:comment "If a DigitalArtifact explicitly tracks ArtifactHistory resources, it should usually identify one as the current/default history.";
  rdfs:label "currentArtifactHistory should usually be present when histories exist";
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "If a DigitalArtifact has explicit sflo:hasArtifactHistory values, it SHOULD also identify one sflo:currentArtifactHistory.";
    sh:select "PREFIX sflo: <https://semantic-flow.github.io/sflo/ontology/>\nSELECT DISTINCT $this WHERE {\n  $this sflo:hasArtifactHistory ?h .\n  FILTER NOT EXISTS { $this sflo:currentArtifactHistory ?current }\n}";
    sh:severity sh:Warning
  ];
  sh:targetClass sflo:DigitalArtifact .

<https://semantic-flow.github.io/sflo/ontology/shacl/ResourcePagePresentationConfigShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A ResourcePagePresentationConfig names the built-in or governed presentation assets and panel selections that a renderer can resolve.";
  rdfs:label "ResourcePagePresentationConfig structure";
  sh:property [
    sh:class sfcfg:OuterResourcePageTemplate;
    sh:maxCount 1;
    sh:message "A ResourcePagePresentationConfig MUST have exactly one sfcfg:hasOuterResourcePageTemplate value typed sfcfg:OuterResourcePageTemplate.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasOuterResourcePageTemplate;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:InnerResourcePageTemplate;
    sh:maxCount 1;
    sh:message "A ResourcePagePresentationConfig MUST have exactly one sfcfg:hasInnerResourcePageTemplate value typed sfcfg:InnerResourcePageTemplate.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasInnerResourcePageTemplate;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ResourcePageStylesheet;
    sh:message "A ResourcePagePresentationConfig SHOULD have at least one sfcfg:hasResourcePageStylesheet value typed sfcfg:ResourcePageStylesheet.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasResourcePageStylesheet;
    sh:severity sh:Warning
  ];
  sh:property [
    sh:class sfcfg:ResourcePagePanelSelection;
    sh:message "A ResourcePagePresentationConfig SHOULD have at least one sfcfg:hasResourcePagePanelSelection value typed sfcfg:ResourcePagePanelSelection.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasResourcePagePanelSelection;
    sh:severity sh:Warning
  ];
  sh:targetClass sfcfg:ResourcePagePresentationConfig .

<https://semantic-flow.github.io/sflo/ontology/shacl/ResourcePagePanelSelectionShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A ResourcePagePanelSelection selects one known panel and constrains when that code-backed panel applies.";
  rdfs:label "ResourcePagePanelSelection structure";
  sh:property [
    sh:class sfcfg:ResourcePagePanel;
    sh:maxCount 1;
    sh:message "A ResourcePagePanelSelection MUST have exactly one sfcfg:hasResourcePagePanel value typed sfcfg:ResourcePagePanel.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasResourcePagePanel;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:datatype xsd:nonNegativeInteger;
    sh:maxCount 1;
    sh:message "A ResourcePagePanelSelection MUST have exactly one non-negative sfcfg:panelOrder value.";
    sh:minCount 1;
    sh:path sfcfg:panelOrder;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ResourcePagePanelInclusionPolicy;
    sh:maxCount 1;
    sh:message "A ResourcePagePanelSelection MUST have exactly one sfcfg:hasPanelInclusionPolicy value typed sfcfg:ResourcePagePanelInclusionPolicy.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasPanelInclusionPolicy;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ResourcePageKind;
    sh:message "If present, sfcfg:hasPanelTargetPageKind values must be IRIs typed sfcfg:ResourcePageKind.";
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasPanelTargetPageKind;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class rdfs:Class;
    sh:message "If present, sfcfg:hasPanelTargetClass values must be IRIs typed rdfs:Class.";
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasPanelTargetClass;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ArtifactRole;
    sh:message "If present, sfcfg:hasPanelTargetArtifactRole values must be IRIs typed sfcfg:ArtifactRole.";
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasPanelTargetArtifactRole;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ResourcePagePanelDataRequirement;
    sh:message "A ResourcePagePanelSelection MUST have at least one sfcfg:hasPanelDataRequirement value typed sfcfg:ResourcePagePanelDataRequirement.";
    sh:minCount 1;
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasPanelDataRequirement;
    sh:severity sh:Violation
  ];
  sh:targetClass sfcfg:ResourcePagePanelSelection .

<https://semantic-flow.github.io/sflo/ontology/shacl/ResourcePageDefinitionPresentationConfigShape>
  rdf:type sh:NodeShape;
  rdfs:comment "A ResourcePageDefinition may opt into adjacent presentation config and selected generated ResourcePage panels.";
  rdfs:label "ResourcePageDefinition presentation config";
  sh:property [
    sh:class sfcfg:ResourcePagePresentationConfig;
    sh:maxCount 1;
    sh:message "If present, sfcfg:hasResourcePagePresentationConfig must be a single IRI typed sfcfg:ResourcePagePresentationConfig.";
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasResourcePagePresentationConfig;
    sh:severity sh:Violation
  ];
  sh:property [
    sh:class sfcfg:ResourcePagePanelSelection;
    sh:message "If present, sfcfg:hasGeneratedResourcePagePanelSelection values must be IRIs typed sfcfg:ResourcePagePanelSelection.";
    sh:nodeKind sh:IRI;
    sh:path sfcfg:hasGeneratedResourcePagePanelSelection;
    sh:severity sh:Violation
  ];
  sh:sparql [
    rdf:type sh:SPARQLConstraint;
    sh:message "A ResourcePageDefinition with sfcfg:hasGeneratedResourcePagePanelSelection MUST also declare sfcfg:hasResourcePagePresentationConfig.";
    sh:select "PREFIX sfcfg: <https://semantic-flow.github.io/sflo/config/>\nSELECT $this WHERE {\n  $this sfcfg:hasGeneratedResourcePagePanelSelection ?selection .\n  FILTER NOT EXISTS { $this sfcfg:hasResourcePagePresentationConfig ?config }\n}";
    sh:severity sh:Violation
  ];
  sh:targetClass sflo:ResourcePageDefinition .
History
ontology/shacl/releases sflo:ArtifactHistory
v0.2.0 sflo:HistoricalState
ttl sflo:ArtifactManifestation