glam/.opencode/rules/DEPRECATED-slot-naming-convention-rico-style.md
2026-02-04 00:24:46 +01:00

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_* and is_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 in schemas/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* or isOrWas* 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_mappings to 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:

  1. Have their own identity - "Amsterdam" is an entity, not just a string
  2. Can have temporal properties - Cities change names, boundaries, administrative status
  3. Can have relationships - A locality is part of a region, which is part of a country
  4. 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:

  1. Future extensibility - Today's string might become tomorrow's class
  2. Consistency - One pattern for all relationships simplifies understanding
  3. 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

  • Ontology Design Patterns (ODP): Recommends modeling values as first-class citizens when they have independent identity

  • LinkML Best Practices: Encourages class-based modeling for reusability and semantic precision

  • FHIR Data Types Pattern: Healthcare interoperability standards model addresses as structured objects with typed components

  • GLEIF Legal Entity Identifier (LEI): Models addresses with typed components for global business entity identification


12. Summary Decision Rules

When to Use has_or_had_*

ALWAYS use has_or_had_* / is_or_was_* when:

  1. The range is (or could become) a class
  2. 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 change
  • has_founding_date - An organization's founding date is fixed
  • has_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


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)