Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions shapes/annotation.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix oa: <http://www.w3.org/ns/oa#> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@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 vs: <http://www.w3.org/2003/06/sw-vocab-status/ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

@prefix annotation_shape: <https://solidproject.org/shapes/annotation#> .

annotation_shape:AnnotationShape
a sh:NodeShape ;
sh:targetClass oa:Annotation ;
sh:name "Annotation Shape" ;
sh:description "SHACL shape describing a Web Annotation covering common note, bookmark, comment, and highlight cases." ;
dct:created "2026-04-23"^^xsd:date ;
vs:term_status "testing" ;
dc:source <https://www.w3.org/TR/annotation-vocab/> ;
prov:wasDerivedFrom <https://www.w3.org/TR/annotation-vocab/> ;
dct:references <http://www.w3.org/ns/oa#Annotation> ; # references the oa:Annotation class from the W3C Web Annotation Vocabulary, which is the expected type of an annotation node, together with the oa:hasBody, oa:hasTarget, oa:bodyValue, and oa:motivatedBy properties used to relate an annotation to its body, target, textual body content, and motivation respectively.
dct:references <https://www.w3.org/TR/annotation-model/> ; # references the W3C Web Annotation Data Model recommendation which defines the conceptual model used by the Web Annotation Vocabulary.
sh:codeIdentifier "Annotation";

# Type: an annotation should be typed as oa:Annotation.
sh:property [
sh:path rdf:type ;
sh:hasValue oa:Annotation ;
sh:name "Type" ;
sh:description "The RDF type of the annotation, which must include oa:Annotation." ;
sh:codeIdentifier "type";
] ;

# Inline textual body (for simple comment / note cases).
# Per the Web Annotation Data Model (§3.2.4), an annotation SHOULD NOT
# have both oa:bodyValue and oa:hasBody. This shape does not enforce
# the exclusion (favouring interoperability) but consumers SHOULD
# populate only one of the two.
sh:property [
sh:path oa:bodyValue ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
sh:name "Body Value" ;
sh:description "Textual content of the annotation body, used for plain-text comments or notes (Web Annotation Vocabulary, oa:bodyValue). SHOULD NOT be combined with oa:hasBody on the same annotation." ;
sh:codeIdentifier "bodyValue";
Comment on lines +41 to +46
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oa:bodyValue is constrained to xsd:string, which will fail validation for language-tagged literals (datatype rdf:langString) that are common for user-generated annotation text. Consider allowing both xsd:string and rdf:langString (e.g., with sh:or), or relaxing the datatype constraint to improve interoperability with Web Annotation data in the wild.

Copilot uses AI. Check for mistakes.
] ;

# External or embedded body resource(s) (for richer body cases).
sh:property [
sh:path oa:hasBody ;
sh:nodeKind sh:BlankNodeOrIRI ;
sh:name "Body" ;
sh:description "A resource that provides the content of the annotation (Web Annotation Vocabulary, oa:hasBody). May be repeated when an annotation has multiple bodies. SHOULD NOT be combined with oa:bodyValue on the same annotation." ;
sh:codeIdentifier "hasBody";
] ;

# Target(s) the annotation is about. Required for an annotation to be meaningful.
sh:property [
sh:path oa:hasTarget ;
sh:nodeKind sh:BlankNodeOrIRI ;
sh:minCount 1 ;
sh:name "Target" ;
sh:description "The resource (or specific resource) that the annotation is about (Web Annotation Vocabulary, oa:hasTarget)." ;
sh:codeIdentifier "hasTarget";
] ;

# Motivation. Left open to the full Web Annotation motivation set so that
# the shape does not reject conforming annotations using motivations
# outside the common note-taking subset (e.g., oa:tagging, oa:replying).
# Apps focused on notes/bookmarks/highlights typically use oa:bookmarking,
# oa:commenting, or oa:highlighting.
sh:property [
sh:path oa:motivatedBy ;
sh:nodeKind sh:IRI ;
sh:name "Motivation" ;
sh:description "Why the annotation was created (Web Annotation Vocabulary, oa:Motivation). Common note-taking values are oa:bookmarking, oa:commenting, and oa:highlighting." ;
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The oa:motivatedBy property description cites oa:Motivation (the class) even though the constraint is on oa:motivatedBy (the property). Updating the description to reference the property (and optionally the expected range) would avoid confusion for shape consumers.

Suggested change
sh:description "Why the annotation was created (Web Annotation Vocabulary, oa:Motivation). Common note-taking values are oa:bookmarking, oa:commenting, and oa:highlighting." ;
sh:description "Why the annotation was created (Web Annotation Vocabulary, oa:motivatedBy). Values are typically motivation IRIs such as oa:bookmarking, oa:commenting, and oa:highlighting." ;

Copilot uses AI. Check for mistakes.
sh:codeIdentifier "motivatedBy";
] ;

# Creator. Permits a WebID IRI or an embedded agent description, matching
# the Web Annotation Data Model which allows the creator to be referenced
# or described inline. Cardinality is left open as the data model permits
# multiple creators (co-creation).
sh:property [
sh:path dct:creator ;
sh:nodeKind sh:BlankNodeOrIRI ;
sh:name "Creator" ;
sh:description "The agent that created the annotation. Typically a WebID IRI in Solid, but the Web Annotation Data Model also permits an embedded agent description and multiple creators." ;
sh:codeIdentifier "creator";
] ;

# Created date.
sh:property [
sh:path dct:created ;
sh:datatype xsd:dateTime ;
sh:maxCount 1 ;
sh:name "Created" ;
sh:description "The date and time at which the annotation was created." ;
sh:codeIdentifier "created";
] ;

# Last modified date.
sh:property [
sh:path dct:modified ;
sh:datatype xsd:dateTime ;
sh:maxCount 1 ;
sh:name "Modified" ;
sh:description "The date and time at which the annotation was last modified." ;
sh:codeIdentifier "modified";
] .
Loading