glam/.opencode/SLOT_NAMING_CONVENTIONS.md
2025-11-21 22:12:33 +01:00

351 lines
10 KiB
Markdown

# Slot Naming Conventions for Hyper-Modular Schema
**Version**: 0.1.0
**Date**: 2025-11-21
**Schema**: Heritage Custodian Observation and Reconstruction Pattern
---
## Overview
In a hyper-modular schema where each slot is defined in its own file, we need clear conventions for handling **semantically identical slots with different ranges** (typing constraints).
**Problem**: The same ontological property (e.g., `prov:wasRevisionOf`) may need to be applied to multiple classes with different range constraints:
- `was_revision_of` for `CustodianReconstruction` → previous `CustodianReconstruction`
- `was_revision_of` for `Record` → previous `Record`
- `was_revision_of` for `Collection` → previous `Collection`
**Solution**: Co-locate range-specialized variants in the **same file**, using hyphenated suffixes to distinguish slot names.
---
## Naming Convention
### Rule 1: Base Slot Name = Primary/First Use Case
The **first defined slot** uses the base name without suffix:
```yaml
# File: modules/slots/was_revision_of.yaml
slots:
was_revision_of:
slot_uri: prov:wasRevisionOf
range: CustodianReconstruction # Primary use case
description: "Previous version of this reconstruction (if updated)"
```
**File location**: `/modules/slots/was_revision_of.yaml`
**Slot name**: `was_revision_of` (no suffix)
---
### Rule 2: Range-Specialized Variants = Hyphenated Suffix
Subsequent slots with **different ranges** but the **same `slot_uri`** are added to the **same file** with hyphenated suffixes:
```yaml
# File: modules/slots/was_revision_of.yaml (SAME FILE)
slots:
was_revision_of:
slot_uri: prov:wasRevisionOf
range: CustodianReconstruction
description: "Previous version of this reconstruction (if updated)"
was_revision_of-record:
slot_uri: prov:wasRevisionOf # SAME ontological property
range: Record # DIFFERENT range
description: "Previous version of this record (if updated)"
was_revision_of-collection:
slot_uri: prov:wasRevisionOf
range: Collection
description: "Previous version of this collection (if updated)"
```
**Hyphen placement**: `{base_slot_name}-{range_type_lowercase}`
**Examples**:
- `was_revision_of``CustodianReconstruction` (base)
- `was_revision_of-record``Record` (specialized)
- `was_revision_of-collection``Collection` (specialized)
- `was_revision_of-observation``CustodianObservation` (specialized)
---
### Rule 3: File Organization
**Single file per ontological property**, containing all range variants:
```
modules/slots/
├── was_revision_of.yaml # Contains: was_revision_of, was_revision_of-record, was_revision_of-collection
├── was_derived_from.yaml # Contains: was_derived_from, was_derived_from-entity
├── identifier_scheme.yaml # Contains: identifier_scheme (single variant - no specialization needed)
└── ...
```
**Benefits**:
- ✅ Easy to find all variants of a property (one file)
- ✅ Centralized documentation of ontological alignment
- ✅ Clear relationship between semantically identical slots
- ✅ Reduces file count (59 files vs. potentially 100+ with separate files per variant)
---
## Why Hyphens?
### ✅ Hyphens ARE allowed in LinkML slot names
LinkML permits hyphens (`-`) in slot identifiers:
```python
from linkml_runtime.linkml_model import SlotDefinition
# All valid:
SlotDefinition(name="was_revision_of") # Underscores
SlotDefinition(name="was-revision-of") # Hyphens
SlotDefinition(name="was_revision_of-record") # Mixed
```
### Advantages of Hyphen Suffix Convention
1. **Visual Separation**: `was_revision_of-record` clearly shows:
- `was_revision_of` = base semantic concept
- `-record` = range specialization suffix
2. **Avoids Double Underscores**: Using underscores would create:
-`was_revision_of_record` (ambiguous: is it "was_revision" of "of_record"?)
-`was_revision_of-record` (clear: "was_revision_of" specialized for "record")
3. **Consistency with LinkML Module Names**: LinkML uses hyphens in module IDs:
- `was-revision-of-slot` (module name)
- `was_revision_of-record` (slot name)
---
## Exceptions and Edge Cases
### Exception 1: Single-Range Slots
If a slot is **only used with one range**, no suffix is needed:
```yaml
# File: modules/slots/legal_name.yaml
slots:
legal_name:
slot_uri: cpov:legalName
range: string # Single, unchanging range
description: "Official legal name as registered"
```
**No variants**: `legal_name-string` is NOT needed (strings are the universal default).
---
### Exception 2: Slots with Multiple Acceptable Ranges (Union Types)
If a slot accepts **multiple ranges simultaneously** (union type), use `any_of`:
```yaml
# File: modules/slots/subject.yaml
slots:
subject:
slot_uri: dcterms:subject
any_of:
- range: string
- range: Concept
- range: uriorcurie
description: "Subject or topic (string, SKOS Concept, or URI)"
```
**No hyphenated variants needed**: This is a single polymorphic slot, not multiple specialized slots.
---
### Exception 3: Ontologically Distinct Properties
If two slots have **different `slot_uri` values**, they belong in **separate files**:
```yaml
# File: modules/slots/was_revision_of.yaml
slots:
was_revision_of:
slot_uri: prov:wasRevisionOf # PROV-O revision
# File: modules/slots/replaces.yaml (SEPARATE FILE)
slots:
replaces:
slot_uri: dcterms:replaces # DIFFERENT ontological property
```
Even if both represent "replaces previous version" semantically, different ontology properties = different files.
---
## Implementation Guidelines
### When Adding a New Range Variant
1. **Check if slot file exists**: Look for `modules/slots/{base_slot_name}.yaml`
2. **Check ontological alignment**: Does the new slot use the **same `slot_uri`**?
3. **If YES**: Add to existing file with hyphenated suffix
4. **If NO**: Create new file with appropriate base name
### Example: Adding `was_revision_of-observation`
```bash
# 1. Check existing file
cat modules/slots/was_revision_of.yaml
# 2. Verify slot_uri matches (prov:wasRevisionOf)
# 3. Add new variant to SAME file
```
```yaml
# File: modules/slots/was_revision_of.yaml
id: https://nde.nl/ontology/hc/slot/was_revision_of
name: was-revision-of-slot
imports:
- ../classes/CustodianReconstruction
- ../classes/CustodianObservation # NEW import
slots:
was_revision_of:
slot_uri: prov:wasRevisionOf
range: CustodianReconstruction
description: "Previous version of this reconstruction (if updated)"
was_revision_of-observation: # NEW variant
slot_uri: prov:wasRevisionOf
range: CustodianObservation
description: "Previous version of this observation (if updated)"
```
**Update aggregator** (`modules/slots_all.yaml`):
```yaml
imports:
- slots/was_revision_of # Already imports file (contains both variants now)
```
**No change needed**: Aggregator imports the file, which now contains both slots.
---
## Documentation Requirements
Each slot file containing multiple variants MUST include:
1. **File-level comment** explaining the ontological property
2. **Import statements** for ALL range classes
3. **Slot-level descriptions** distinguishing each variant
**Example**:
```yaml
# CustodianReconstruction Slot: was_revision_of
# PROV-O wasRevisionOf property with multiple range specializations
id: https://nde.nl/ontology/hc/slot/was_revision_of
name: was-revision-of-slot
imports:
- ../classes/CustodianReconstruction
- ../classes/CustodianObservation
- ../classes/Record
- ../classes/Collection
slots:
was_revision_of:
slot_uri: prov:wasRevisionOf
range: CustodianReconstruction
description: >-
Previous version of this reconstruction (if updated).
PROV-O: wasRevisionOf for CustodianReconstruction versioning.
was_revision_of-observation:
slot_uri: prov:wasRevisionOf
range: CustodianObservation
description: >-
Previous version of this observation (if updated).
PROV-O: wasRevisionOf for CustodianObservation versioning.
was_revision_of-record:
slot_uri: prov:wasRevisionOf
range: Record
description: >-
Previous version of this record (if updated).
PROV-O: wasRevisionOf for Record versioning.
comments:
- "All variants map to PROV-O prov:wasRevisionOf"
- "Range specialization enables type-safe versioning"
- "Hyphenated suffix pattern: {base_name}-{range_lowercase}"
```
---
## Rationale: Why Co-location?
### Alternative 1: Separate Files Per Variant ❌
```
modules/slots/
├── was_revision_of.yaml
├── was_revision_of_record.yaml
├── was_revision_of_collection.yaml
├── was_revision_of_observation.yaml
```
**Problems**:
- ❌ File explosion (59 slots → 150+ files with variants)
- ❌ Difficult to see all variants of a property
- ❌ Duplicated ontology documentation across files
- ❌ Import bloat in aggregator
### Alternative 2: Co-location with Hyphenated Suffixes ✅
```
modules/slots/
├── was_revision_of.yaml # Contains all 4 variants
```
**Benefits**:
- ✅ Manageable file count
- ✅ Single source of truth for ontological property
- ✅ Easy maintenance (one file to update)
- ✅ Clear relationship between variants
---
## Summary
| Convention | Rule | Example |
|------------|------|---------|
| **Base slot** | No suffix | `was_revision_of` |
| **Range variant** | Hyphenated suffix | `was_revision_of-record` |
| **File location** | Single file per ontological property | `/slots/was_revision_of.yaml` |
| **File contains** | All range variants | `was_revision_of`, `was_revision_of-record`, ... |
| **Suffix format** | `-{range_lowercase}` | `-record`, `-observation`, `-collection` |
| **When to create variant** | Same `slot_uri`, different `range` | `slot_uri: prov:wasRevisionOf` |
| **When to create new file** | Different `slot_uri` | `prov:wasRevisionOf` vs `dcterms:replaces` |
---
## References
- LinkML Slot Definition: https://linkml.io/linkml/schemas/slots.html
- PROV-O wasRevisionOf: https://www.w3.org/TR/prov-o/#wasRevisionOf
- Heritage Custodian Schema: `/schemas/20251121/linkml/01_custodian_name_modular.yaml`
---
**Last Updated**: 2025-11-21
**Maintainer**: GLAM Data Extraction Project