22 KiB
⚠️ DEPRECATED - Rule 39: Slot Naming Convention (RiC-O Style)
DEPRECATION NOTICE (2026-02-03): This rule has been deprecated. A new naming convention has been adopted. The
has_or_had_*andis_or_was_*patterns are NO LONGER required. Slots should use simple, descriptive verb-based names (e.g.,exhibit,catalogue,contain). See the current slot files inschemas/20251121/linkml/modules/slots/20260202_matang/for examples.
HISTORICAL REFERENCE ONLY - DO NOT FOLLOW
The content below is preserved for historical reference only.
🚨 CRITICAL: LinkML slots representing relational predicates MUST follow RiC-O-style naming conventions to express temporal semantics accurately. DEPRECATED
1. Core Naming Patterns
The Records in Contexts Ontology (RiC-O) uses systematic naming patterns that distinguish between:
- Current-only relationships (simple present tense)
- Current-or-past relationships (temporal ambiguity handled explicitly)
Pattern 1: hasOrHad* (Active Voice, Temporal Ambiguity)
Use when the relationship may or may not still exist:
| Predicate | Meaning |
|---|---|
hasOrHadPart |
Currently has or previously had as a part |
hasOrHadHolder |
Currently has or previously had a holder |
hasOrHadType |
Currently has or previously had a type |
hasOrHadController |
Currently has or previously had a controller |
hasOrHadSubordinate |
Currently has or previously had a subordinate |
hasOrHadLeader |
Currently has or previously had a leader |
hasOrHadSubject |
Currently has or previously had as subject |
hasOrHadMandateType |
Currently has or previously had a mandate type |
Pattern 2: isOrWas* (Passive/Inverse, Temporal Ambiguity)
Use for inverse properties of hasOrHad*:
| Predicate | Meaning |
|---|---|
isOrWasPartOf |
Currently is or was previously part of |
isOrWasHolderOf |
Currently is or was previously holder of |
isOrWasTypeOf |
Currently is or was previously type of |
isOrWasControllerOf |
Currently is or was previously controller of |
isOrWasMemberOf |
Currently is or was previously member of |
isOrWasEmployerOf |
Currently is or was previously employer of |
isOrWasActiveAtDate |
Was active at a specific date |
Pattern 3: Simple Present Tense (Current-Only)
Use when the relationship exists now and is not historical:
| Predicate | Meaning |
|---|---|
hasBeginningDate |
Has a beginning date (current fact) |
hasEndDate |
Has an end date (current fact) |
hasCreationDate |
Has a creation date (current fact) |
hasBirthPlace |
Has a birth place (permanent fact) |
hasDeathPlace |
Has a death place (permanent fact) |
hasRecordSetType |
Has a record set type |
Pattern 4: Transitive and Direct Variants
For hierarchical relationships, RiC-O provides:
| Pattern | Meaning | Example |
|---|---|---|
*Transitive |
Applies transitively through hierarchy | isIncludedInTransitive |
directly* |
Applies only to immediate relationship | directlyIncludes |
isDirectly* |
Inverse of direct relationship | isDirectlyIncludedIn |
2. When to Use Each Pattern
Decision Tree
Is this a relationship that can change over time?
│
├─ YES → Does the relationship have an inverse?
│ │
│ ├─ YES (active voice: subject → object)
│ │ └─ Use hasOrHad{Relationship}
│ │
│ └─ YES (passive/inverse: object → subject)
│ └─ Use isOrWas{Relationship}Of
│
└─ NO (immutable fact, e.g., birth date)
└─ Use simple has{Attribute}
Examples
Temporal Relationship (can change):
# An archive's custodian can change over time
slots:
has_or_had_custodian:
slot_uri: rico:hasOrHadHolder
description: |
The current or former custodian (holder) of this record set.
RiC-O: hasOrHadHolder - "Connects a Record Resource to an Agent
that holds or held it."
Immutable Fact (cannot change):
# A founding date is permanent once established
slots:
founding_date:
slot_uri: rico:hasBeginningDate
description: |
The date when this organization was founded.
RiC-O: hasBeginningDate - "Connects an entity to the date
at which it began to exist."
3. Slot Naming in LinkML
Converting RiC-O Predicates to LinkML Slot Names
| RiC-O Predicate | LinkML Slot Name | Notes |
|---|---|---|
rico:hasOrHadPart |
has_or_had_part |
Snake_case conversion |
rico:isOrWasPartOf |
is_or_was_part_of |
Snake_case conversion |
rico:hasCreationDate |
creation_date |
Drop "has" for attributes |
rico:directlyIncludes |
directly_includes |
Preserve adverb |
Example Slot File
# modules/slots/has_or_had_custodian.yaml
id: https://nde.nl/ontology/hc/slot/has_or_had_custodian
name: has_or_had_custodian_slot
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
rico: https://www.ica.org/standards/RiC/ontology#
schema: http://schema.org/
slots:
has_or_had_custodian:
slot_uri: rico:hasOrHadHolder
description: |
The current or former custodian (holder) of this heritage entity.
RiC-O: hasOrHadHolder - "Connects a Record Resource to an Agent
that holds or held it."
Use this property when custody may have changed over time.
For current-only relationships, use a dated variant with
temporal qualifiers.
range: HeritageCustodian
multivalued: true
exact_mappings:
- schema:maintainer
close_mappings:
- prov:wasAttributedTo
4. Temporal Semantics in Heritage Domain
Heritage Custodianship is Inherently Temporal
Heritage institutions frequently experience:
- Custody transfers (collection moves to new institution)
- Mergers (two archives become one)
- Splits (one library becomes two)
- Name changes (rebranding while entity continues)
- Relocations (same institution, new address)
Therefore, most custodian-related predicates SHOULD use hasOrHad* / isOrWas* patterns.
Permanent vs. Temporal Properties
| Property Type | Pattern | Example |
|---|---|---|
| Permanent facts | has* |
hasBirthPlace, hasFoundingDate |
| Temporal relationships | hasOrHad* |
hasOrHadHolder, hasOrHadLocation |
| Identity properties | has* |
hasIdentifier, hasName |
| Membership | isOrWas* |
isOrWasMemberOf, isOrWasPartOf |
5. Cross-Ontology Alignment
RiC-O to Other Ontologies
| RiC-O | CIDOC-CRM | Schema.org | PROV-O |
|---|---|---|---|
hasOrHadHolder |
P50_has_current_keeper |
maintainer |
wasAttributedTo |
hasOrHadPart |
P46_is_composed_of |
hasPart |
- |
isOrWasPartOf |
P46i_forms_part_of |
isPartOf |
- |
hasBeginningDate |
P82_at_some_time_within |
startDate |
startedAtTime |
hasEndDate |
P82_at_some_time_within |
endDate |
endedAtTime |
When Ontologies Differ
If the base ontology doesn't use RiC-O patterns:
slots:
parent_organization:
slot_uri: org:subOrganizationOf # W3C Org (doesn't use hasOrHad)
description: |
The parent organization in organizational hierarchy.
org:subOrganizationOf - "Represents hierarchical containment
of Organizations or OrganizationalUnits"
Note: Unlike RiC-O, W3C ORG uses simple present tense.
For temporal precision, combine with ChangeEvent tracking.
close_mappings:
- rico:isOrWasPartOf # RiC-O temporal equivalent
6. Anti-Patterns
❌ Inventing Custom Temporal Patterns
# WRONG - Custom pattern, not from any ontology
slots:
was_holder:
slot_uri: hc:wasHolder # ❌ Custom namespace, non-standard pattern
❌ Mixing Patterns Inconsistently
# WRONG - Inconsistent within same schema
slots:
has_or_had_part:
slot_uri: rico:hasOrHadPart # ✅ RiC-O pattern
former_location:
slot_uri: hc:formerLocation # ❌ Different pattern for same concept
✅ Consistent Pattern Usage
# CORRECT - Consistent RiC-O patterns
slots:
has_or_had_part:
slot_uri: rico:hasOrHadPart
has_or_had_location:
slot_uri: rico:hasOrHadLocation # Or use close_mappings to RiC-O
7. Validation Checklist
Before creating a new relational slot:
- Determined if relationship is temporal or permanent
- For temporal: Used
hasOrHad*orisOrWas*pattern - For permanent: Used simple
has*pattern - Checked RiC-O ontology for existing predicate
- If not in RiC-O, checked CIDOC-CRM, Schema.org, PROV-O
- Documented temporal semantics in slot description
- Added
close_mappingsto equivalent predicates in other ontologies - Slot name follows snake_case convention
8. Quick Reference
Most Common RiC-O Predicates for Heritage
| Use Case | Predicate | Inverse |
|---|---|---|
| Custody | hasOrHadHolder |
isOrWasHolderOf |
| Hierarchy | hasOrHadPart |
isOrWasPartOf |
| Subordination | hasOrHadSubordinate |
isOrWasSubordinateTo |
| Control | hasOrHadController |
isOrWasControllerOf |
| Membership | hasOrHadMember |
isOrWasMemberOf |
| Leadership | hasOrHadLeader |
- |
| Subject | hasOrHadSubject |
isOrWasSubjectOf |
| Type | hasOrHadType |
isOrWasTypeOf |
Date Properties (Permanent)
| Use Case | Predicate |
|---|---|
| Start/founding | hasBeginningDate |
| End/closure | hasEndDate |
| Creation | hasCreationDate |
| Accumulation | hasAccumulationDate |
9. Semantic Distinction: Hierarchy vs Association
🚨 CRITICAL: The same deprecated slot name can mask completely different semantic relationships. Always analyze the actual semantic intent before choosing a replacement pattern.
Two Relationship Categories
| Category | Semantic | Pattern | Ontology Source |
|---|---|---|---|
| Organizational Hierarchy | "This org is a child/part of that org" | RiC-O isOrWas* / hasOrHad* |
RiC-O, W3C ORG |
| Event Association | "This event happened at/to that entity" | PROV-O wasAssociatedWith |
PROV-O |
Real-World Example: parent_custodian Disambiguation
The deprecated parent_custodian slot was used in TWO different classes with different semantics:
Case 1: CustodianLegalStatus.parent_custodian → Organizational Hierarchy
# OLD (ambiguous)
CustodianLegalStatus:
slots:
- parent_custodian # "Which org is this legal status for?"
# NEW (precise RiC-O pattern)
CustodianLegalStatus:
slots:
- is_or_was_suborganization_of # Organizational hierarchy relationship
Semantic: "This legal status entity belongs to an organizational hierarchy under a parent organization."
Case 2: OrganizationalChangeEvent.parent_custodian → Event Association
# OLD (ambiguous)
OrganizationalChangeEvent:
slots:
- parent_custodian # "Which org did this event happen to?"
# NEW (precise PROV-O pattern)
OrganizationalChangeEvent:
slots:
- associated_custodian # Event-entity association
Semantic: "This change event was associated with (happened to) a custodian organization."
When to Use Each Pattern
Is this relationship about organizational structure?
│
├─ YES → Does the relationship express containment/hierarchy?
│ │
│ ├─ YES (child → parent)
│ │ └─ Use isOrWas{Relationship}Of (e.g., is_or_was_suborganization_of)
│ │
│ └─ YES (parent → children)
│ └─ Use hasOrHad{Relationship} (e.g., has_or_had_suborganization)
│
└─ NO → Is this about events/activities affecting an entity?
│
├─ YES (event → entity it affected)
│ └─ Use PROV-O pattern: associated_custodian (prov:wasAssociatedWith)
│
└─ YES (entity → events that affected it)
└─ Use PROV-O pattern: has_associated_event (prov:wasInfluencedBy)
The associated_custodian Slot (PROV-O Pattern)
For event-to-entity associations, use the PROV-O wasAssociatedWith predicate:
# modules/slots/associated_custodian.yaml
slots:
associated_custodian:
slot_uri: prov:wasAssociatedWith
description: |
The heritage custodian that this event or activity was associated with.
PROV-O: wasAssociatedWith - "An activity association is an assignment
of responsibility to an agent for an activity, indicating that the
agent had a role in the activity."
Use this for event-to-custodian associations, NOT for organizational
hierarchy. For hierarchy relationships, use is_or_was_suborganization_of
or has_or_had_suborganization instead.
range: HeritageCustodian
10. Migration Mapping Table
The following deprecated slots have been migrated to RiC-O/PROV-O patterns:
Organizational Hierarchy Slots
| Deprecated Slot | RiC-O Replacement | slot_uri | Inverse |
|---|---|---|---|
parent_custodian (hierarchy context) |
is_or_was_suborganization_of |
rico:isOrWasSubordinateTo |
has_or_had_suborganization |
has_suborganization |
has_or_had_suborganization |
rico:hasOrHadSubordinate |
is_or_was_suborganization_of |
parent_collection |
is_or_was_sub_collection_of |
rico:isOrWasPartOf |
has_or_had_sub_collection |
sub_collections |
has_or_had_sub_collection |
rico:hasOrHadPart |
is_or_was_sub_collection_of |
Custody/Collection Slots
| Deprecated Slot | RiC-O Replacement | slot_uri | Inverse |
|---|---|---|---|
has_collection |
has_or_had_collection |
rico:hasOrHadPart |
is_or_was_collection_of |
collection_of |
is_or_was_collection_of |
rico:isOrWasPartOf |
has_or_had_collection |
Network/Membership Slots
| Deprecated Slot | RiC-O Replacement | slot_uri | Inverse |
|---|---|---|---|
encompassing_body |
is_or_was_encompassed_by |
rico:isOrWasPartOf |
encompasses_or_encompassed |
encompasses |
encompasses_or_encompassed |
rico:hasOrHadPart |
is_or_was_encompassed_by |
has_member |
has_or_had_member |
rico:hasOrHadMember |
is_or_was_member_of |
is_member_of |
is_or_was_member_of |
rico:isOrWasMemberOf |
has_or_had_member |
Platform/Ownership Slots
| Deprecated Slot | RiC-O Replacement | slot_uri | Inverse |
|---|---|---|---|
platform_of |
is_or_was_platform_of |
rico:isOrWasHolderOf |
- |
Event Association Slots (PROV-O)
| Deprecated Slot | PROV-O Replacement | slot_uri | Notes |
|---|---|---|---|
parent_custodian (event context) |
associated_custodian |
prov:wasAssociatedWith |
NOT hierarchy - event association |
Deprecation Status
All deprecated slots remain in the schema with:
deprecated: "Use {replacement} instead"deprecated_element_has_exact_replacement: {replacement_slot}
This allows existing data to validate while encouraging migration to new patterns.
11. Class Promotion Principle: All Values Can Become Classes
🚨 CRITICAL: Use has_or_had_* naming for ALL relational slots, not just "object properties." Values that appear to be simple strings (like locality, region, country) SHOULD be modeled as classes because they represent real-world entities that:
- Have their own identity - "Amsterdam" is an entity, not just a string
- Can have temporal properties - Cities change names, boundaries, administrative status
- Can have relationships - A locality is part of a region, which is part of a country
- Can be enriched - GeoNames ID, coordinates, ISO codes, multilingual labels
The Class Promotion Pattern
Instead of treating address components as string values:
# ❌ ANTI-PATTERN: String values
slots:
locality:
range: string
slot_uri: vcard:locality
region:
range: string
slot_uri: vcard:region
Model them as classes with proper relationships:
# ✅ CORRECT: Class-based modeling
classes:
Locality:
description: A city, town, village, or other populated place
class_uri: locn:Geometry # Or schema:Place
slots:
- long_name
- short_name
- geonames_id
- iso_code
- is_or_was_part_of_region
slots:
has_or_had_locality:
range: Locality
slot_uri: rico:hasOrHadLocation
description: The locality (city/town) associated with this address
Address Components as Classes
ALL address-related values should be modeled as classes:
| String Slot (Deprecated) | Class | Relationship Slot |
|---|---|---|
locality |
Locality |
has_or_had_locality |
region |
Region |
has_or_had_region |
country_name |
Country |
has_or_had_country |
house_number |
HouseNumber |
has_or_had_house_number |
street_name |
StreetName |
has_or_had_street_name |
postal_code |
PostalCode |
has_or_had_postal_code |
address_type |
AddressType |
has_or_had_address_type |
Class Hierarchy for Address Components
Following Rule 0b (Type/Types naming convention):
# AddressComponentType.yaml (abstract base)
classes:
AddressComponentType:
abstract: true
description: Base class for address component types
# AddressComponentTypes.yaml (concrete subclasses)
classes:
HouseNumber:
is_a: AddressComponentType
StreetName:
is_a: AddressComponentType
Locality:
is_a: AddressComponentType
Region:
is_a: AddressComponentType
Country:
is_a: AddressComponentType
PostalCode:
is_a: AddressComponentType
Why Universal has_or_had_* Naming
Even for seemingly "permanent" relationships, use temporal naming because:
- Future extensibility - Today's string might become tomorrow's class
- Consistency - One pattern for all relationships simplifies understanding
- Temporal reality - Even "permanent" facts can have temporal qualifiers:
- A building's street name can change
- A locality can merge with another
- A country can split (Yugoslavia → multiple countries)
- Postal codes are reassigned
Best Practice References
This approach aligns with established ontology design patterns:
-
RiC-O (Records in Contexts): Uses
hasOrHad*for ALL relationships that can change over time, including locations and organizational structures- Reference: ICA RiC-O Documentation
-
Ontology Design Patterns (ODP): Recommends modeling values as first-class citizens when they have independent identity
- Reference: Ontology Design Patterns Portal
-
LinkML Best Practices: Encourages class-based modeling for reusability and semantic precision
- Reference: LinkML Documentation
-
FHIR Data Types Pattern: Healthcare interoperability standards model addresses as structured objects with typed components
- Reference: HL7 FHIR Address DataType
-
GLEIF Legal Entity Identifier (LEI): Models addresses with typed components for global business entity identification
- Reference: GLEIF Data Quality
12. Summary Decision Rules
When to Use has_or_had_*
ALWAYS use has_or_had_* / is_or_was_* when:
- The range is (or could become) a class
- The relationship involves:
- Locations (addresses, places, regions)
- Organizations (custodians, departments)
- People (staff, directors, contributors)
- Collections (record sets, holdings)
- Types/Categories (any classification)
- Identifiers (ISIL, VIAF, GeoNames ID)
Only use simple has_* for truly immutable attributes:
has_birth_date- A person's birth date cannot changehas_founding_date- An organization's founding date is fixedhas_creation_timestamp- Record creation time is immutable
Quick Reference Table
| Relationship Type | Naming Pattern | Example |
|---|---|---|
| Location → Address | has_or_had_address |
Address can change |
| Address → Locality | has_or_had_locality |
Locality is a class |
| Address → Region | has_or_had_region |
Region is a class |
| Address → Country | has_or_had_country |
Country is a class |
| Custodian → Type | has_or_had_custodian_type |
Type can change |
| Person → Affiliation | has_or_had_affiliation |
Affiliation changes |
| Record → Identifier | has_or_had_identifier |
Identifiers can be reassigned |
| Entity → Birth Date | has_birth_date |
Immutable fact |
See Also
- RiC-O Ontology:
/data/ontology/RiC-O_1-1.rdf - PROV-O Ontology: W3C PROV-O
- Rule 38: Slot Centralization and Semantic URI Requirements
- Rule 1: Ontology Files Are Your Primary Reference
- Rule 0b: Type/Types File Naming Convention
- RiC-O Official Documentation
- Ontology Design Patterns Portal
- LinkML Best Practices
- GLEIF Data Quality Standards
Version: 1.2.0 Created: 2026-01-08 Updated: 2026-01-12 Author: OpenCODE Changelog:
- v1.2.0: Added Section 11 (Class Promotion Principle) and Section 12 (Summary Decision Rules) with best practice references
- v1.1.0: Added Section 9 (Semantic Distinction) and Section 10 (Migration Mapping Table)