glam/.opencode/rules/linkml/slot-centralization-and-semantic-uri-rule.md
kempersc 6e63465196 Add ImageTilingServiceEndpoint class and archive ID class
- Introduced the ImageTilingServiceEndpoint class for tiled high-resolution image delivery, including deep-zoom and transformation capabilities, with multilingual descriptions and structured aliases.
- Archived the ID class as a backwards-compatible alias for Identifier, marking it as deprecated to enforce the use of the canonical Identifier model.
2026-02-15 21:40:13 +01:00

10 KiB

Rule 38: Slot Centralization and Semantic URI Requirements

🚨 CRITICAL: All LinkML slots MUST be centralized in model/symbolic/schema/modules/slots/ and MUST have semantically sound slot_uri predicates from base ontologies.


1. Slot Centralization is Mandatory

Location: All slot definitions MUST be in model/symbolic/schema/modules/slots/

File Naming: {slot_name}.yaml (snake_case)

Import Pattern: Classes import slots via relative imports:

# In modules/classes/Collection.yaml
imports:
  - ../slots/collection_name
  - ../slots/collection_type_ref
  - ../slots/parent_collection

Why Centralization?

  1. UML Visualization: The frontend's schema service loads slots from the database in which modules/slots/ files are ingested to determine aggregation edges. Inline slots in class files are NOT properly parsed for visualization.

  2. Reusability: Slots can be used by multiple classes without duplication.

  3. Semantic Consistency: Single source of truth for slot semantics prevents drift.

  4. Maintainability: Changes to slot semantics propagate automatically to all classes.

Anti-Pattern: Inline Slot Definitions

# ❌ WRONG - Slots defined inline in class file
classes:
  Collection:
    slots:
      - collection_name
      - parent_collection
    
slots:  # ← This section in a class file is WRONG
  collection_name:
    range: string
# ✅ CORRECT - Slots imported from centralized files
# In modules/classes/Collection.yaml
imports:
  - ../slots/collection_name
  - ../slots/parent_collection

classes:
  Collection:
    slots:
      - collection_name
      - parent_collection

2. Every Slot MUST Have slot_uri

slot_uri provides the semantic meaning of the slot in a linked data context. It maps your slot to a predicate from an established ontology. Do avoid adding external uri in case there are no exact mapping! In this common case, the slot_uri should be a self-reference using the 'hc' prefix.

Required Slot File Structure

# Global slot definition for {slot_name}
# Used by: {list of classes}

id: https://nde.nl/ontology/hc/slot/{slot_name}
name: {slot_name}

prefixes:
  linkml: https://w3id.org/linkml/
  hc: https://nde.nl/ontology/hc/
  # Add ontology prefixes as needed
  rico: https://www.ica.org/standards/RiC/ontology#
  schema: http://schema.org/
  skos: http://www.w3.org/2004/02/skos/core#

slots:
  {slot_name}:
    slot_uri: {ontology_prefix}:{predicate}  # ← REQUIRED
    description: |
      Description of the slot's semantic meaning.
      
      {OntologyName}: {predicate} - "{definition from ontology}"      
    range: {ClassName or primitive}
    required: true/false
    multivalued: true/false
    # Optional mappings for additional semantic relationships
    exact_mappings:
      - schema:alternatePredicate
    close_mappings:
      - dct:relatedPredicate
    examples:
      - value: {example}
        description: {explanation}

Ontology Sources for slot_uri

Consult these base ontology files in /data/ontology/:

Ontology File Namespace Use Cases
RiC-O RiC-O_1-1.rdf rico: Archival records, record sets, custody
CIDOC-CRM CIDOC_CRM_v7.1.3.rdf crm: Cultural heritage objects, events
Schema.org schemaorg.owl schema: Web semantics, general properties
SKOS skos.rdf skos: Labels, concepts, mappings
Dublin Core dublin_core_elements.rdf dcterms: Metadata properties
PROV-O prov-o.ttl prov: Provenance tracking
PAV pav.rdf pav: Provenance, authoring, versioning
TOOI tooiont.ttl tooi: Dutch government organizations
CPOV core-public-organisation-ap.ttl cpov: EU public sector
ORG org.rdf org: Organizations, units, roles
FOAF foaf.ttl foaf: People, agents, social network
GLEIF gleif_base.ttl gleif_base: Legal entities

Example: Correct Slot with slot_uri

# modules/slots/preferred_label.yaml
id: https://nde.nl/ontology/hc/slot/preferred_label
name: preferred_label_slot

prefixes:
  linkml: https://w3id.org/linkml/
  hc: https://nde.nl/ontology/hc/
  skos: http://www.w3.org/2004/02/skos/core#
  schema: http://schema.org/
  rdfs: http://www.w3.org/2000/01/rdf-schema#

slots:
  preferred_label:
    slot_uri: skos:prefLabel  # ← REQUIRED
    description: |
      The primary display name for this entity.
      
      SKOS: prefLabel - "A preferred lexical label for a resource."
      
      This is the CANONICAL name - the standardized label accepted by the 
      entity itself for public representation.      
    range: string
    required: false
    exact_mappings:
      - schema:name
      - rdfs:label
    examples:
      - value: "Rijksmuseum"
        description: Primary display name for the Rijksmuseum

3. Mappings Can Apply to Both Classes AND Slots

LinkML provides SKOS-based mapping predicates that work on both classes and slots:

Mapping Type Predicate Use Case
exact_mappings skos:exactMatch Identical meaning
close_mappings skos:closeMatch Very similar meaning
related_mappings skos:relatedMatch Semantically related
narrow_mappings skos:narrowMatch More specific
broad_mappings skos:broadMatch More general

When to Use Mappings vs. slot_uri

Scenario Use
Primary semantic identity slot_uri (exactly one)
Equivalent predicates in other ontologies exact_mappings (multiple allowed)
Similar but not identical predicates close_mappings
Related predicates with different scope narrow_mappings / broad_mappings

Example: Slot with Multiple Mappings

slots:
  website:
    slot_uri: gleif_base:hasWebsite  # Primary predicate
    range: uri
    description: |
      Official website URL of the organization or entity.
      
      gleif_base:hasWebsite - "A website associated with something"      
    exact_mappings:
      - schema:url  # Identical meaning in Schema.org
    close_mappings:
      - foaf:homepage  # Similar but specifically "main" page

Example: Class with Multiple Mappings

classes:
  Collection:
    class_uri: rico:RecordSet  # Primary class
    exact_mappings:
      - crm:E78_Curated_Holding  # CIDOC-CRM equivalent
    close_mappings:
      - bf:Collection  # BIBFRAME close match
    narrow_mappings:
      - edm:ProvidedCHO  # Europeana (narrower - cultural heritage objects)

4. Workflow for Creating a New Slot

Step 1: Search Base Ontologies

Before creating a slot, search for existing predicates:

# Search for relevant predicates
rg "website|homepage|url" /data/ontology/*.ttl /data/ontology/*.rdf /data/ontology/*.owl

# Check specific ontology
rg "rdfs:label|rdfs:comment" /data/ontology/schemaorg.owl | grep -i "name"

Step 2: Document Ontology Alignment

In the slot file, document WHY you chose that predicate:

slots:
  source_url:
    slot_uri: pav:retrievedFrom
    description: |
      URL of the web page from which data was retrieved.
      
      pav:retrievedFrom - "The URI from which the resource was retrieved."
      
      Chosen over:
      - schema:url (too generic - refers to the entity's URL, not source)
      - dct:source (refers to intellectual source, not retrieval location)
      - prov:wasDerivedFrom (refers to entity derivation, not retrieval)      

Step 3: Create Centralized Slot File

# Create new slot file
touch schemas/20251121/linkml/modules/slots/new_slot_name.yaml

Step 4: Update Manifest

Run the manifest regeneration script or manually add to manifest:

cd schemas/20251121/linkml
python3 scripts/regenerate_manifest.py

Step 5: Import in Class Files

Add the import to classes that use this slot.


5. Validation Checklist

Before committing slot changes:

  • Slot file is in modules/slots/
  • Slot has slot_uri pointing to an established ontology predicate
  • Predicate is from data/ontology/ files or standard vocabularies
  • Description includes ontology definition
  • Rationale documented if multiple predicates were considered
  • exact_mappings/close_mappings added for equivalent predicates
  • Manifest updated to include new slot file
  • Classes using the slot have been updated with import
  • Frontend slot files synced: frontend/public/schemas/20251121/linkml/modules/slots/

6. Common Slot URI Mappings

Slot Concept Recommended slot_uri Alternative Mappings
Preferred name skos:prefLabel schema:name, rdfs:label
Alternative names skos:altLabel schema:alternateName
Description dcterms:description schema:description, rdfs:comment
Identifier dcterms:identifier schema:identifier
Website URL gleif_base:hasWebsite schema:url, foaf:homepage
Source URL pav:retrievedFrom prov:wasDerivedFrom
Created date dcterms:created schema:dateCreated, prov:generatedAtTime
Modified date dcterms:modified schema:dateModified
Language schema:inLanguage dcterms:language
Part of dcterms:isPartOf rico:isOrWasPartOf, schema:isPartOf
Has part dcterms:hasPart rico:hasOrHadPart, schema:hasPart
Location schema:location locn:address, crm:P53_has_former_or_current_location
Start date schema:startDate prov:startedAtTime, rico:hasBeginningDate
End date schema:endDate prov:endedAtTime, rico:hasEndDate

See Also


Version: 1.0.0 Created: 2026-01-06 Author: OpenCODE