glam/.opencode/rules/full-slot-migration-rule.md

373 lines
12 KiB
Markdown

# Rule 53: Full Slot Migration - No Deprecation Notes
🚨 **CRITICAL**: When migrating slots from `slot_fixes.yaml`:
1. **Follow the `revision` section EXACTLY** - The `slot_fixes.yaml` file specifies the exact replacement slots and classes to use
2. **Perform FULL MIGRATION** - Completely remove the deprecated slot from the entity class
3. **Do NOT add deprecation notes** - Never keep both old and new slots with deprecation markers
---
## 🚨 slot_fixes.yaml is AUTHORITATIVE AND CURATED 🚨
**File Location**: `schemas/20251121/linkml/modules/slots/slot_fixes.yaml`
**THIS FILE IS THE SINGLE SOURCE OF TRUTH FOR ALL SLOT MIGRATIONS.**
The `slot_fixes.yaml` file has been **manually curated** to specify the exact replacement slots and classes for each deprecated slot. The revisions are based on:
1. **Ontology analysis** - Each replacement was chosen based on alignment with base ontologies (CIDOC-CRM, RiC-O, PROV-O, Schema.org, etc.)
2. **Semantic correctness** - Revisions reflect the intended meaning of the original slot
3. **Pattern consistency** - Follows established naming conventions (Rule 39: RiC-O style, Rule 43: singular nouns)
4. **Class hierarchy design** - Type/Types pattern (Rule 0b) applied where appropriate
**YOU MUST NOT**:
- ❌ Substitute different slots than those specified in `revision`
- ❌ Use your own judgment to pick "similar" slots
- ❌ Skip the revision and invent new mappings
- ❌ Partially apply the revision (e.g., use the slot but not the class)
**YOU MUST**:
- ✅ Follow the `revision` section TO THE LETTER
- ✅ Use EXACTLY the slots and classes specified
- ✅ Apply ALL components of the revision (both slots AND classes)
- ✅ Interpret `link_branch` fields correctly (see below)
- ✅ Update `processed.status: true` after completing migration
---
## Understanding `link_branch` in Revision Plans
🚨 **CRITICAL**: The `link_branch` field in revision plans indicates **nested class attributes**. Items with `link_branch: N` are slots/classes that belong TO the primary class, not standalone replacements.
### How to Interpret `link_branch`
| Revision Item | Meaning |
|---------------|---------|
| Items **WITHOUT** `link_branch` | **PRIMARY** slot and class to create |
| Items **WITH** `link_branch: 1` | First attribute branch that the primary class needs |
| Items **WITH** `link_branch: 2` | Second attribute branch that the primary class needs |
| Items **WITH** `link_branch: N` | Nth attribute branch for the primary class |
### Example: `visitor_count` Revision
```yaml
- original_slot_id: https://nde.nl/ontology/hc/slot/visitor_count
revision:
- label: has_or_had_quantity # PRIMARY SLOT (no link_branch)
type: slot
- label: Quantity # PRIMARY CLASS (no link_branch)
type: class
- label: has_or_had_measurement_unit # Quantity needs this slot
type: slot
link_branch: 1 # ← Branch 1: unit attribute
- label: MeasureUnit # Range of has_or_had_measurement_unit
type: class
value:
- visitors
link_branch: 1
- label: temporal_extent # Quantity needs this slot too
type: slot
link_branch: 2 # ← Branch 2: time attribute
- label: TimeSpan # Range of temporal_extent
type: class
link_branch: 2
```
**Interpretation**: This creates:
1. **Primary**: `has_or_had_quantity` slot → `Quantity` class
2. **Branch 1**: `Quantity.has_or_had_measurement_unit``MeasureUnit` (with value "visitors")
3. **Branch 2**: `Quantity.temporal_extent``TimeSpan`
### Resulting Class Structure
```yaml
# The Quantity class should have these slots:
Quantity:
slots:
- has_or_had_measurement_unit # From link_branch: 1
- temporal_extent # From link_branch: 2
```
### Complex Example: `visitor_conversion_rate`
```yaml
- original_slot_id: https://nde.nl/ontology/hc/slot/visitor_conversion_rate
revision:
- label: has_or_had_conversion_rate # PRIMARY SLOT
type: slot
- label: ConversionRate # PRIMARY CLASS
type: class
- label: has_or_had_type # ConversionRate.has_or_had_type
type: slot
link_branch: 1
- label: ConversionRateType # Abstract type class
type: class
link_branch: 1
- label: includes_or_included # ConversionRateType hierarchy slot
type: slot
link_branch: 1
- label: ConversionRateTypes # Concrete subclasses file
type: class
link_branch: 1
- label: temporal_extent # ConversionRate.temporal_extent
type: slot
link_branch: 2
- label: TimeSpan # Range of temporal_extent
type: class
link_branch: 2
```
**Interpretation**:
1. **Primary**: `has_or_had_conversion_rate``ConversionRate`
2. **Branch 1**: Type hierarchy with `ConversionRateType` (abstract) + `ConversionRateTypes` (concrete subclasses)
3. **Branch 2**: Temporal tracking via `temporal_extent``TimeSpan`
### Migration Checklist for `link_branch` Revisions
- [ ] Create/verify PRIMARY slot exists
- [ ] Create/verify PRIMARY class exists
- [ ] For EACH `link_branch: N`:
- [ ] Add the branch slot to PRIMARY class's `slots:` list
- [ ] Import the branch slot file
- [ ] Import the branch class file (if creating new class)
- [ ] Verify range of branch slot points to branch class
- [ ] Update consuming class to use PRIMARY slot (not deprecated slot)
- [ ] Update examples to show nested structure
---
## Mandatory: Follow slot_fixes.yaml Revisions Exactly
**The `revision` section in `slot_fixes.yaml` is AUTHORITATIVE.** Do not substitute different slots based on your own judgment.
**Example from slot_fixes.yaml**:
```yaml
- original_slot_id: https://nde.nl/ontology/hc/slot/actual_start
revision:
- label: begin_of_the_begin # ← USE THIS SLOT
type: slot
- label: TimeSpan # ← USE THIS CLASS
type: class
```
**CORRECT**: Use `begin_of_the_begin` slot (as specified)
**WRONG**: Substitute `has_actual_start_date` (not in revision)
## The Problem
Adding deprecation notes while keeping both old and new slots:
- Creates schema bloat with redundant properties
- Confuses data consumers about which slot to use
- Violates single-source-of-truth principle
- Complicates future data validation
## Anti-Pattern (WRONG)
```yaml
# WRONG - Keeping deprecated slot with deprecation note
classes:
TemporaryLocation:
slots:
- actual_start # OLD - kept with deprecation note
- actual_end # OLD - kept with deprecation note
- has_actual_start_date # NEW
- has_actual_end_date # NEW
slot_usage:
actual_start:
deprecated: |
DEPRECATED: Use has_actual_start_date instead.
# ... more deprecation documentation
```
## Correct Pattern
```yaml
# CORRECT - Only new slots, old slots completely removed
classes:
TemporaryLocation:
slots:
- has_actual_start_date # NEW - only new slots present
- has_actual_end_date # NEW
# NO slot_usage for deprecated slots - they don't exist in this class
```
## Migration Steps
When processing a slot from `slot_fixes.yaml`:
1. **Identify affected entity class(es)**
2. **Remove old slot from imports** (if dedicated import file exists)
3. **Remove old slot from slots list**
4. **Remove any slot_usage for old slot**
5. **Add new slot import** (if not already present)
6. **Add new slot to slots list**
7. **Add slot_usage for new slot** (if range override or customization needed)
8. **Update examples** to use new slot
9. **Validate with gen-owl**
## What Happens to Old Slot Files
The old slot files in `modules/slots/` (e.g., `actual_start.yaml`, `activities_societies.yaml`) are **NOT deleted** because:
- Other entity classes might still use them
- They serve as documentation of the old schema
- They can be archived when all usages are migrated
However, the old slots are **removed from the entity class** being migrated.
## Example: TemporaryLocation Migration
**Before** (with old slots):
```yaml
imports:
- ../slots/actual_end
- ../slots/actual_start
- ../slots/has_actual_start_date
- ../slots/has_actual_end_date
slots:
- actual_end
- actual_start
- has_actual_start_date
- has_actual_end_date
```
**After** (fully migrated):
```yaml
imports:
# actual_end and actual_start imports REMOVED
- ../slots/has_actual_start_date
- ../slots/has_actual_end_date
slots:
# actual_end and actual_start REMOVED from list
- has_actual_start_date
- has_actual_end_date
```
## Slot Usage for New Slots
Only add `slot_usage` for the new slot if you need to:
- Override the range for this specific class
- Add class-specific examples
- Add class-specific constraints
Do NOT add `slot_usage` just to document that it replaces an old slot.
## Recording in slot_fixes.yaml
When marking a slot as processed:
```yaml
- original_slot_id: https://nde.nl/ontology/hc/slot/actual_start
processed:
status: true
timestamp: '2026-01-14T16:00:00Z'
session: "session-2026-01-14-type-migration"
notes: "FULLY MIGRATED: TemporaryLocation - actual_start REMOVED, using temporal_extent with TimeSpan.begin_of_the_begin (Rule 53)"
```
Note the "FULLY MIGRATED" prefix in notes to confirm this was a complete removal, not a deprecation-in-place.
---
## ⚠️ Common Mistakes to Avoid ⚠️
### Mistake 1: Substituting Different Slots
**slot_fixes.yaml specifies**:
```yaml
- original_slot_id: https://nde.nl/ontology/hc/slot/actual_start
revision:
- label: begin_of_the_begin # ← MUST USE THIS
type: slot
- label: TimeSpan # ← WITH THIS CLASS
type: class
```
| Action | Status |
|--------|--------|
| Using `begin_of_the_begin` with `TimeSpan` | ✅ CORRECT |
| Using `has_actual_start_date` (invented) | ❌ WRONG |
| Using `start_date` (different slot) | ❌ WRONG |
| Using `begin_of_the_begin` WITHOUT `TimeSpan` | ❌ WRONG (incomplete) |
### Mistake 2: Partial Application
The revision often specifies MULTIPLE components that work together:
```yaml
revision:
- label: has_or_had_type # ← Slot for linking
type: slot
- label: BackupType # ← Abstract base class
type: class
- label: includes_or_included # ← Slot for hierarchy
type: slot
- label: BackupTypes # ← Concrete subclasses
type: class
```
**All four components** are part of the migration. Don't just use `has_or_had_type` and ignore the class structure.
### Mistake 3: Using `temporal_extent` Slot Correctly
When `slot_fixes.yaml` specifies TimeSpan-based revision:
```yaml
revision:
- label: begin_of_the_begin
type: slot
- label: TimeSpan
type: class
```
This means: **Use the `temporal_extent` slot** (which has `range: TimeSpan`) and access the temporal bounds via TimeSpan's slots:
```yaml
# CORRECT: Use temporal_extent with TimeSpan structure
temporal_extent:
begin_of_the_begin: '2020-06-15'
end_of_the_end: '2022-03-15'
# WRONG: Create new has_actual_start_date slot
has_actual_start_date: '2020-06-15' # ❌ Not in revision!
```
### Mistake 4: Not Updating Examples
When migrating slots, **update ALL examples** in the class file:
- Description examples (in class description)
- slot_usage examples
- Class-level examples (at bottom of file)
---
## Verification Checklist
Before marking a slot as processed:
- [ ] Read the `revision` section completely
- [ ] Identified ALL slots and classes in revision
- [ ] Removed old slot from imports
- [ ] Removed old slot from slots list
- [ ] Removed old slot from slot_usage
- [ ] Added new slot(s) per revision
- [ ] Added new class import(s) per revision
- [ ] Updated ALL examples to use new slots
- [ ] Validated with `linkml-lint` or `gen-owl`
- [ ] Updated `slot_fixes.yaml` with:
- `status: true`
- `timestamp` (ISO 8601)
- `session` identifier
- `notes` with "FULLY MIGRATED:" prefix
---
## See Also
- Rule 9: Enum-to-Class Promotion (single source of truth principle)
- Rule 0b: Type/Types File Naming Convention
- Rule: Slot Naming Convention (Current Style)
- `.opencode/ENUM_TO_CLASS_PRINCIPLE.md`
- `schemas/20251121/linkml/modules/slots/slot_fixes.yaml` - **AUTHORITATIVE** master list of migrations