# Resource Linking System - Checkpoint 2 Addendum **Author:** Resource Linking Architect **Date:** 2026-02-12 **Status:** Draft for Checkpoint 2 Review **Version:** 0.3 **Addresses:** Checkpoint 1 feedback from Sienna --- ## Changes from Checkpoint 1 This addendum addresses two required changes from Sienna's Checkpoint 1 review: 1. **Template Merging Support** (instead of all-or-nothing replacement) 2. **Dual Operator Support** (`==` and `is` both work) --- ## 1. Template Merging Design ### 1.1 The Problem **Original Design (Rejected):** ```storybook template Worker { uses behaviors: [HandleBasicNeeds, RestWhenTired] } character Martha: Human from Worker { uses behaviors: [BakeryWork] // REPLACES Worker behaviors entirely } // Result: Only [BakeryWork] - lost HandleBasicNeeds and RestWhenTired ``` **Requested Behavior:** Martha should get BOTH Worker behaviors AND her own behaviors, composed together. ### 1.2 Proposed Merge Algorithm **Merge Semantics:** - Template links and character links are **concatenated** - Character links come **first** (evaluated before template links) (no template links come first since that's the "base class") - If same behavior appears in both, character's version **overrides** template's version - Priority and conditions are preserved from each source **Example:** ```storybook template Worker { uses behaviors: [ { tree: HandleBasicNeeds, priority: critical } { tree: RestWhenTired, priority: normal } { tree: Idle, priority: low } ] } character Martha: Human from Worker { uses behaviors: [ { tree: BakeryWork, priority: normal } { tree: Idle, priority: normal } // Overrides Worker's Idle ] } // Merged result: // [ // { tree: BakeryWork, priority: normal } // From Martha // { tree: Idle, priority: normal } // From Martha (overrides template) // { tree: HandleBasicNeeds, priority: critical } // From Worker // { tree: RestWhenTired, priority: normal } // From Worker // ] ``` ### 1.3 Merge Rules **Rule 1: Concatenation** - Merge = `character_links ++ template_links` - Character links are evaluated first at runtime (higher precedence) **Rule 2: Override by Name** - If character defines a link to behavior `X`, template's link to `X` is ignored - Match by behavior tree name (not by priority or condition) - Allows character to "replace" template's version of a behavior **Rule 3: Preserve Priority** - Priority values are NOT automatically adjusted - Character can define same behavior with different priority than template - Runtime selection uses declared priorities as-is **Rule 4: Preserve Conditions** - Conditions are NOT merged with boolean operators - Each link keeps its own condition - If both have same behavior with different conditions, character's wins (override) **Rule 5: Default Behavior** - If both character and template define `default: true` for same link type, character's default wins - Warning issued if both define defaults (even for different behaviors) ### 1.4 Conflict Resolution **Conflict Type 1: Same Behavior, Different Priority** ```storybook template Worker { uses behaviors: [ { tree: Rest, priority: high } ] } character Martha: Human from Worker { uses behaviors: [ { tree: Rest, priority: normal } // Character overrides ] } // Result: Martha's Rest(normal) wins, template's Rest(high) ignored ``` **Conflict Type 2: Same Behavior, Different Conditions** ```storybook template Worker { uses behaviors: [ { tree: Work, when: employed } ] } character Martha: Human from Worker { uses behaviors: [ { tree: Work, when: at_bakery } // Character overrides ] } // Result: Martha's Work(when: at_bakery) wins ``` **Conflict Type 3: Multiple Defaults** ```storybook template Worker { uses behaviors: [ { tree: Idle, default: true } ] } character Martha: Human from Worker { uses behaviors: [ { tree: Rest, default: true } // Warning! ] } // Warning: Both template and character define default behaviors // Resolution: Character's default wins (Rest), template's Idle kept but not default ``` ### 1.5 Multi-Level Template Inheritance Templates can inherit from templates: ```storybook template LivingBeing { uses behaviors: [ { tree: Breathe, priority: critical } { tree: Eat, priority: high } ] } template Worker from LivingBeing { uses behaviors: [ { tree: Work, priority: normal } { tree: Rest, priority: normal } ] } character Martha: Human from Worker { uses behaviors: [ { tree: BakeryWork, priority: normal } ] } // Merge chain: LivingBeing -> Worker -> Martha // Result (in evaluation order): // [ // BakeryWork(normal), // Martha // Work(normal), // Worker // Rest(normal), // Worker // Breathe(critical), // LivingBeing // Eat(high) // LivingBeing // ] ``` **Merge Algorithm for Multi-Level:** 1. Start with deepest template (LivingBeing) 2. Merge next template (Worker) on top 3. Merge character on top 4. At each level, apply override-by-name rule ### 1.6 Explicit Override Syntax (Future Extension) If we want more control, we could add explicit `override` keyword: ```storybook character Martha: Human from Worker { uses behaviors: [ { tree: Rest, priority: low, override: true } // Explicit override { tree: BakeryWork, priority: normal } ] } ``` **Recommendation:** Start without `override` keyword. Add later if needed. ### 1.7 Empty Array Semantics **Question:** What if character defines empty array? ```storybook template Worker { uses behaviors: [HandleBasicNeeds, Rest] } character Martha: Human from Worker { uses behaviors: [] // Empty! } ``` **Option A:** Empty array means "use no behaviors" (clear template behaviors) **Option B:** Empty array is treated as "not defined", inherit from template **Recommendation:** **Option B** - Only non-empty arrays trigger override/merge. Empty arrays are same as omitted field. (empty array just means "empty array", i think if it's a zero-length array then we would treat it as "not defined") ### 1.8 Updated Merge Pseudocode ```rust fn merge_behavior_links( character_links: Vec, template_links: Vec ) -> Vec { let mut result = Vec::new(); let mut seen_behaviors = HashSet::new(); // Add character links first (higher precedence) for link in character_links { let behavior_name = link.tree.join("::"); seen_behaviors.insert(behavior_name.clone()); result.push(link); } // Add template links that don't conflict for link in template_links { let behavior_name = link.tree.join("::"); if !seen_behaviors.contains(&behavior_name) { result.push(link); } // else: skip, character already defined this behavior } result } fn merge_character_with_template( char: Character, template: Template ) -> ResolvedCharacter { // Recursively merge if template has parent template let template_links = if let Some(parent_template) = template.parent { merge_behavior_links(template.behavior_links, parent_template.behavior_links) } else { template.behavior_links }; // Merge character on top of (potentially already merged) template let behavior_links = if !char.behavior_links.is_empty() { merge_behavior_links(char.behavior_links, template_links) } else { template_links // Character doesn't define any, inherit all }; // Same logic for schedule_links... ResolvedCharacter { behavior_links, schedule_links, ... } } ``` ### 1.9 Validation Rules **New Validations:** 1. **Warn on default conflicts:** ``` warning: multiple default behaviors defined ┌─ characters/martha.sb:8:5 │ 8 │ uses behaviors: [ 9 │ { tree: Rest, default: true } │ = note: template 'Worker' also defines a default behavior: Idle = help: only one default is used at runtime (character's takes precedence) ``` 2. **Warn on priority conflicts (optional):** ``` warning: behavior priority changed from template ┌─ characters/martha.sb:9:9 │ 9 │ { tree: Rest, priority: low } │ = note: template 'Worker' defines Rest with priority: high = help: character's priority (low) will override template's (high) ``` ### 1.10 Examples **Example 1: Simple Composition** ```storybook template Villager { uses behaviors: [ { tree: BasicNeeds, priority: critical } { tree: Socialize, priority: normal } { tree: Sleep, priority: normal } ] } character Martha: Human from Villager { uses behaviors: [ { tree: BakeryWork, priority: normal } ] } // Martha gets: [BakeryWork, BasicNeeds, Socialize, Sleep] ``` **Example 2: Selective Override** ```storybook template Villager { uses behaviors: [ { tree: BasicNeeds, priority: critical } { tree: Sleep, priority: normal } ] uses schedule: VillagerSchedule } character Martha: Human from Villager { uses behaviors: [ { tree: Sleep, priority: low } // Override just Sleep ] uses schedule: BakerSchedule // Override schedule } // Behaviors: [Sleep(low), BasicNeeds(critical)] // Schedule: BakerSchedule (replaces VillagerSchedule) ``` **Example 3: Multi-Level Inheritance** ```storybook template Mortal { uses behaviors: [{ tree: Age, priority: critical }] } template Worker from Mortal { uses behaviors: [{ tree: Work, priority: normal }] } template Baker from Worker { uses behaviors: [{ tree: BakeBreed, priority: normal }] } character Martha: Human from Baker { uses behaviors: [{ tree: ManageBakery, priority: normal }] } // Result: [ManageBakery, BakeBread, Work, Age] ``` --- ## 2. Dual Operator Support (`==` and `is`) ### 2.1 The Request Sienna wants both `==` and `is` to work for equality comparisons (like Python). **Examples:** ```storybook uses behaviors: [ { tree: Giant, when: current_size == huge } // C-style { tree: Tiny, when: current_size is tiny } // Python-style ] ``` ### 2.2 Semantic Equivalence Both operators mean **equality test**: - `current_size == huge` → true if current_size equals huge - `current_size is tiny` → true if current_size equals tiny - No distinction between "identity" and "equality" (like Python's `is` vs `==`) - They're syntactic alternatives for readability ### 2.3 Grammar Update ```lalrpop // In parser.lalrpop CompOp: CompOp = { ">=" => CompOp::Gte, "<=" => CompOp::Lte, "==" => CompOp::Eq, "is" => CompOp::Eq, // NEW - maps to same enum variant "!=" => CompOp::Neq, ">" => CompOp::Gt, "<" => CompOp::Lt, }; ``` **AST Representation:** ```rust pub enum CompOp { Eq, // Both == and is map to this Neq, Lt, Gt, Lte, Gte, } ``` No AST change needed - both `==` and `is` produce the same `CompOp::Eq`. ### 2.4 Negation **Question:** Should we support `is not` as well as `!=`? (yes i want this) ```storybook when: current_size is not huge // Python style when: current_size != huge // C style ``` **Recommendation:** **Yes**, support `is not` for consistency with Python: ```lalrpop CompOp: CompOp = { ">=" => CompOp::Gte, "<=" => CompOp::Lte, "==" => CompOp::Eq, "is" => CompOp::Eq, "is" "not" => CompOp::Neq, // NEW "!=" => CompOp::Neq, ">" => CompOp::Gt, "<" => CompOp::Lt, }; ``` ### 2.5 Updated Examples **All valid:** ```storybook uses behaviors: [ { tree: Giant, when: size == huge } { tree: Giant, when: size is huge } { tree: Tiny, when: size != tiny } { tree: Tiny, when: size is not tiny } ] ``` ### 2.6 Documentation Update Update language docs to show both styles: > **Condition Expressions** > > Use `when:` clauses to specify conditions for link selection: > > ```storybook > when: emotional_state == frightened // C-style equality > when: emotional_state is frightened // Python-style equality > when: size != normal // C-style inequality > when: size is not normal // Python-style inequality > ``` > > Both `==` and `is` mean equality. Both `!=` and `is not` mean inequality. > Choose whichever reads more naturally for your condition. --- ## 3. Updated Implementation Plan ### Phase 1: AST Extension (Week 1) - UNCHANGED No changes from original design. ### Phase 2: Parser Implementation (Week 1-2) - UPDATED **Original:** 1. Implement `BehaviorLinkStmt` and `ScheduleLinkStmt` grammar 2. Implement `BehaviorLinkSpec` and `ScheduleLinkSpec` parsing 3. Add link parsing to character, institution, template productions 4. Write parser tests for all link variations **Updated:** 1. Implement `BehaviorLinkStmt` and `ScheduleLinkStmt` grammar 2. Implement `BehaviorLinkSpec` and `ScheduleLinkSpec` parsing 3. Add link parsing to character, institution, template productions 4. **Add `is` and `is not` to `CompOp` production** ← NEW 5. Write parser tests for all link variations 6. **Write tests for both `==`/`is` and `!=`/`is not`** ← NEW ### Phase 3: Resolution (Week 2) - UPDATED **Original:** 1. Implement behavior/schedule name resolution in `resolve/names.rs` 2. Add priority validation 3. Add condition expression validation 4. Implement template merge logic for links in `resolve/merge.rs` 5. Write resolution tests **Updated:** 1. Implement behavior/schedule name resolution in `resolve/names.rs` 2. Add priority validation 3. Add condition expression validation 4. **Implement template merge algorithm with override-by-name** ← UPDATED 5. **Add multi-level template merge support** ← NEW 6. **Add validation warnings for default/priority conflicts** ← NEW 7. Write resolution tests 8. **Write tests for merge edge cases** ← NEW ### Phase 4: Resolved Types (Week 2) - UNCHANGED No changes from original design. ### Phase 5: Validation & Diagnostics (Week 3) - UPDATED **Original:** 1. Implement semantic validation (single default, etc.) 2. Add helpful error messages with fuzzy matching 3. Add warnings for incomplete condition coverage 4. Write validation tests **Updated:** 1. Implement semantic validation (single default, etc.) 2. Add helpful error messages with fuzzy matching 3. Add warnings for incomplete condition coverage 4. **Add warnings for merge conflicts (defaults, priorities)** ← NEW 5. Write validation tests ### Phase 6: Integration & Documentation (Week 3) - UPDATED **Original:** 1. Update examples to use new linking syntax 2. Update language documentation 3. Run full test suite 4. Create migration examples (if backward compatibility breaks) **Updated:** 1. Update examples to use new linking syntax 2. **Update examples to show template merging** ← NEW 3. **Update examples to show both `==` and `is` operators** ← NEW 4. Update language documentation 5. **Document merge algorithm for users** ← NEW 6. Run full test suite 7. Create migration examples (if backward compatibility breaks) **Total Estimate:** Still 3 weeks (merge adds complexity but not significant time) --- ## 4. Updated Success Criteria ### Must Have - [x] Unified `uses` keyword for behaviors and schedules - [x] Single-link syntax works (`uses behavior: Name`) - [x] Multi-link syntax works (`uses behaviors: [...]`) - [x] Conditions supported (`when: expression`) - [x] **Both `==` and `is` operators supported** ← NEW - [x] **Both `!=` and `is not` operators supported** ← NEW - [x] Priorities supported for behaviors - [x] Default fallback supported (`default: true`) - [x] **Template merging works (concatenation + override)** ← UPDATED - [x] **Multi-level template inheritance works** ← NEW - [x] Character linking works - [x] Institution linking works - [x] Parser produces correct AST - [x] Resolver validates references - [x] **Warnings for merge conflicts** ← NEW - [x] Clear error messages - [x] SBIR format defined --- ## 5. Open Questions for Sienna (Checkpoint 2) ### Question 1: Empty Array Semantics ("not defined) What should this mean? ```storybook template Worker { uses behaviors: [HandleNeeds, Rest] } character Martha: Human from Worker { uses behaviors: [] // Empty array! } ``` **Option A:** Empty = clear template behaviors (Martha has NO behaviors) **Option B:** Empty = ignore, inherit from template (same as omitting field) **Recommendation:** Option B ### Question 2: Priority Conflict Warnings (lsp warning is more than fine, yellow squiggly, internal behavior is to override at the character level) Should we warn when character changes priority of template behavior? ```storybook template Worker { uses behaviors: [{ tree: Rest, priority: high }] } character Martha from Worker { uses behaviors: [{ tree: Rest, priority: low }] } ``` **Option A:** Silent override (no warning) **Option B:** Warn about priority change **Option C:** Error (must use same priority) **Recommendation:** Option A (silent) or Option B (warning) ### Question 3: `is not` Support (absolutely yes) Should we support `is not` alongside `!=`? **Recommendation:** Yes, for consistency with Python ### Question 4: Merge Validation Level (um... strict is prolly best tbh) How strict should merge conflict validation be? **Option A:** Errors on any conflict (strict) **Option B:** Warnings only (permissive) **Option C:** Silent (trust user) **Recommendation:** Option B (warnings) --- ## 6. Summary of Changes ### From Checkpoint 1 to Checkpoint 2: **Major Change 1: Template Merging** - ❌ Old: All-or-nothing replacement - ✅ New: Concatenation with override-by-name - Character links evaluated first - Template links added if not overridden - Multi-level inheritance supported **Major Change 2: Operator Support** - ✅ Added: `is` as synonym for `==` - ✅ Added: `is not` as synonym for `!=` - No AST changes needed - Parser accepts both styles **Minor Changes:** - Added merge conflict warnings - Updated examples to show composition - Updated implementation plan phases --- **End of Checkpoint 2 Addendum** **Next Step:** Review with user (Sienna) for approval, then proceed to implementation (Task #9).