# Rule 38: Slot Centralization and Semantic URI Requirements 🚨 **CRITICAL**: All LinkML slots MUST be centralized in `schemas/20251121/linkml/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 `schemas/20251121/linkml/modules/slots/` **File Naming**: `{slot_name}.yaml` (snake_case) **Import Pattern**: Classes import slots via relative imports: ```yaml # 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 `modules/slots/` 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 ```yaml # ❌ 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 ``` ```yaml # ✅ 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. ### Required Slot File Structure ```yaml # 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` ```yaml # 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 ```yaml 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 ```yaml 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: ```bash # 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: ```yaml 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 ```bash # 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: ```bash 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 - [LinkML slot_uri documentation](https://linkml.io/linkml-model/latest/docs/slot_uri/) - [LinkML mappings documentation](https://linkml.io/linkml-model/latest/docs/mappings/) - [LinkML URIs and Mappings guide](https://linkml.io/linkml/schemas/uris-and-mappings.html) - Rule 1: Ontology Files Are Your Primary Reference - Rule 0: LinkML Schemas Are the Single Source of Truth --- **Version**: 1.0.0 **Created**: 2026-01-06 **Author**: OpenCODE