# Template Merge Implementation Notes **Author:** Resource Linking Architect **Date:** 2026-02-12 **Purpose:** Implementation planning for Phase 3 (Resolution) --- ## Core Merge Algorithm ### Data Structures ```rust // In resolve/merge.rs /// Tracks behavior links during merge to detect overrides struct BehaviorMergeContext { seen_behaviors: HashMap, // behavior_name -> link result: Vec, warnings: Vec, } enum MergeWarning { DefaultConflict { character_behavior: String, template_behavior: String, }, PriorityConflict { behavior: String, character_priority: Priority, template_priority: Priority, }, } ``` ### Merge Function Signature ```rust pub fn merge_behavior_links( character_links: Vec, template_links: Vec, character_name: &str, template_name: &str, ) -> Result<(Vec, Vec), MergeError> { // Implementation } ``` ### Step-by-Step Algorithm ```rust fn merge_behavior_links( character_links: Vec, template_links: Vec, character_name: &str, template_name: &str, ) -> Result<(Vec, Vec), MergeError> { let mut ctx = BehaviorMergeContext { seen_behaviors: HashMap::new(), result: Vec::new(), warnings: Vec::new(), }; // Step 1: Add all character links (highest precedence) for link in character_links { let behavior_name = link.tree.join("::"); // Check for duplicate behavior in character itself if ctx.seen_behaviors.contains_key(&behavior_name) { return Err(MergeError::DuplicateBehavior { behavior: behavior_name, source: character_name.to_string(), }); } ctx.seen_behaviors.insert(behavior_name.clone(), link.clone()); ctx.result.push(link); } // Step 2: Add template links that aren't overridden for link in template_links { let behavior_name = link.tree.join("::"); if let Some(char_link) = ctx.seen_behaviors.get(&behavior_name) { // Character overrides this behavior - check for conflicts // Check priority conflict if let (Some(char_pri), Some(tmpl_pri)) = (&char_link.priority, &link.priority) { if char_pri != tmpl_pri { ctx.warnings.push(MergeWarning::PriorityConflict { behavior: behavior_name.clone(), character_priority: char_pri.clone(), template_priority: tmpl_pri.clone(), }); } } // Skip template link (already have character's version) continue; } // No override, add template link ctx.result.push(link); } // Step 3: Check for default conflicts let char_defaults: Vec<_> = ctx.result.iter() .filter(|link| link.is_default) .take(2) // Only need to check if >1 .collect(); if char_defaults.len() > 1 { ctx.warnings.push(MergeWarning::DefaultConflict { // ... warning details }); } Ok((ctx.result, ctx.warnings)) } ``` ### Multi-Level Template Merge ```rust pub fn resolve_template_hierarchy( character: &Character, templates: &HashMap, ) -> Result<(Vec, Vec), MergeError> { // Step 1: Build template chain (deepest first) let mut template_chain = Vec::new(); let mut current_template_name = character.template.clone(); while let Some(tmpl_name) = current_template_name { let template = templates.get(&tmpl_name) .ok_or(MergeError::UnresolvedTemplate { name: tmpl_name.clone() })?; template_chain.push(template); current_template_name = template.parent_template.clone(); } // Step 2: Merge from deepest template up to character template_chain.reverse(); // Now [deepest, ..., direct parent] let mut merged_behaviors = Vec::new(); let mut merged_schedules = Vec::new(); for template in template_chain { let (behaviors, warnings) = merge_behavior_links( merged_behaviors, template.behavior_links.clone(), character.name, &template.name, )?; merged_behaviors = behaviors; // TODO: emit warnings // Same for schedules... } // Step 3: Merge character on top let (final_behaviors, warnings) = merge_behavior_links( character.behavior_links.clone(), merged_behaviors, character.name, "character", )?; Ok((final_behaviors, merged_schedules)) } ``` --- ## Complex Merge Scenarios to Test ### Scenario 1: Simple Override ```storybook template Worker { uses behaviors: [A, B, C] } character Martha from Worker { uses behaviors: [B, D] // Override B, add D } // Expected: [B(Martha), D, A, C] // A and C come from Worker, B is Martha's version ``` ### Scenario 2: Multi-Level with Overrides ```storybook template Base { uses behaviors: [A, B] } template Mid from Base { uses behaviors: [B, C] // Override B from Base } character Char from Mid { uses behaviors: [C, D] // Override C from Mid } // Merge order: // 1. Base: [A, B(Base)] // 2. Mid merges on Base: [B(Mid), C, A] // 3. Char merges on Mid: [C(Char), D, B(Mid), A] ``` ### Scenario 3: Priority Changes Through Chain ```storybook template Base { uses behaviors: [{ tree: Rest, priority: high }] } template Mid from Base { uses behaviors: [{ tree: Rest, priority: normal }] // Warning! } character Char from Mid { uses behaviors: [{ tree: Rest, priority: low }] // Warning! } // Expected: [Rest(low)] // Warnings: // - Mid changed Rest priority from high to normal // - Char changed Rest priority from normal to low ``` ### Scenario 4: Condition Overrides ```storybook template Worker { uses behaviors: [ { tree: Work, when: employed } ] } character Martha from Worker { uses behaviors: [ { tree: Work, when: at_bakery } // Override condition ] } // Expected: [Work(when: at_bakery)] // Template's Work(when: employed) is completely replaced ``` ### Scenario 5: Default Conflicts ```storybook template Worker { uses behaviors: [ { tree: Idle, default: true } ] } character Martha from Worker { uses behaviors: [ { tree: Rest, default: true } ] } // Expected: [Rest(default), Idle(not default)] // Warning: Both template and character define defaults ``` ### Scenario 6: Empty Array Edge Case ```storybook template Worker { uses behaviors: [A, B, C] } character Martha from Worker { uses behaviors: [] // What does this mean? } // If empty = "clear all": [] // If empty = "ignore, inherit": [A, B, C] // Waiting for Sienna's decision (Question 1) ``` ### Scenario 7: Diamond Inheritance (Not Supported) ```storybook template A { uses behaviors: [X] } template B from A { uses behaviors: [Y] } template C from A { uses behaviors: [Z] } template D from B, C { ... } // ERROR: Multiple inheritance not supported ``` **Decision:** Single inheritance only (one parent template max). --- ## Error Message Design ### Error: Duplicate Behavior in Same Context ``` error: duplicate behavior definition ┌─ characters/martha.sb:10:9 │ 10 │ { tree: BakeryWork, priority: normal } │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 11 │ { tree: BakeryWork, priority: high } │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definition │ = note: behavior 'BakeryWork' is already defined in this character = help: remove one of the duplicate definitions or use different behaviors ``` ### Warning: Priority Conflict in Template Merge ``` warning: behavior priority changed from template ┌─ characters/martha.sb:9:9 │ 9 │ { tree: Rest, priority: low } │ ^^^^^^^^^^^^^^^^ │ = note: template 'Worker' defines 'Rest' with priority 'high' = note: character's priority (low) will override template's (high) = help: if this is intentional, no action needed ``` ### Warning: Multiple Defaults ``` warning: multiple default behaviors defined ┌─ characters/martha.sb:10:9 │ 10 │ { tree: Rest, default: true } │ ^^^^^^^^^^^^^^^ │ = note: template 'Worker' also defines a default behavior: 'Idle' = note: character's default (Rest) will be used; template's (Idle) remains as non-default = help: only one default is active at runtime ``` ### Error: Unresolved Template ``` error: unresolved template reference ┌─ characters/martha.sb:1:25 │ 1 │ character Martha from Workerr { │ ^^^^^^^ template 'Workerr' not found │ = help: did you mean 'Worker'? (defined in templates/worker.sb) ``` ### Warning: Condition Override ``` warning: behavior condition changed from template ┌─ characters/martha.sb:9:9 │ 9 │ { tree: Work, when: at_bakery } │ ^^^^^^^^^^^^^^^^^^^ │ = note: template 'Worker' defines 'Work' with condition 'employed' = note: character's condition will replace template's condition ``` --- ## Testing Strategy ### Unit Tests ```rust #[cfg(test)] mod merge_tests { use super::*; #[test] fn test_simple_merge() { let char_links = vec![ behavior_link("BakeryWork", None, None), ]; let tmpl_links = vec![ behavior_link("BasicNeeds", Some(Priority::Critical), None), behavior_link("Rest", Some(Priority::Normal), None), ]; let (result, warnings) = merge_behavior_links( char_links, tmpl_links, "Martha", "Worker" ).unwrap(); assert_eq!(result.len(), 3); assert_eq!(result[0].tree, "BakeryWork"); assert_eq!(result[1].tree, "BasicNeeds"); assert_eq!(result[2].tree, "Rest"); assert!(warnings.is_empty()); } #[test] fn test_override_priority() { let char_links = vec![ behavior_link("Rest", Some(Priority::Low), None), ]; let tmpl_links = vec![ behavior_link("Rest", Some(Priority::High), None), ]; let (result, warnings) = merge_behavior_links( char_links, tmpl_links, "Martha", "Worker" ).unwrap(); assert_eq!(result.len(), 1); assert_eq!(result[0].priority, Some(Priority::Low)); assert_eq!(warnings.len(), 1); assert!(matches!(warnings[0], MergeWarning::PriorityConflict { .. })); } #[test] fn test_multi_level_merge() { // Base -> Mid -> Char // Test that merging works through chain } #[test] fn test_default_conflict() { // Both template and character define defaults } #[test] fn test_empty_array() { // Waiting for Sienna's decision on semantics } } ``` ### Integration Tests ```rust #[test] fn test_resolve_alice_wonderland() { // Load Alice example with WonderlandCreature template // Verify merged behaviors are correct } #[test] fn test_three_level_inheritance() { // Mortal -> Worker -> Baker -> Martha // Verify all behaviors present and priorities correct } ``` --- ## Performance Considerations ### Merge Complexity - Single template: O(n + m) where n=char links, m=template links - k-level templates: O(k * (n + m)) - Typical case: k=1-3, n+m < 20, negligible overhead ### Optimization Opportunities 1. **Cache merged templates**: If Template X is used by multiple characters, cache its fully-merged result 2. **Early termination**: If character defines no links, skip merge entirely 3. **Lazy merging**: Only merge when links are actually accessed at runtime **Decision:** Start with simple O(k*n) implementation. Optimize only if profiling shows bottleneck. --- ## Edge Cases Checklist - [x] Character overrides all template behaviors - [x] Character overrides some template behaviors - [x] Character adds new behaviors to template - [x] Multi-level template chain - [x] Priority conflicts through chain - [x] Condition overrides - [x] Multiple defaults - [ ] Empty array semantics (awaiting Sienna) - [x] Diamond inheritance (not supported, single parent only) - [x] Circular template references (should be caught in validation) - [x] Template references non-existent parent (error) - [x] Behavior name resolution fails (error) --- **Status:** Implementation notes complete, awaiting Checkpoint 2 approval to begin coding.