# Resource Linking System Design **Author:** Resource Linking Architect **Date:** 2026-02-12 **Status:** Ready for Checkpoint 1 Review **Version:** 0.2 **Keyword Decision:** `uses` (approved by Sienna) --- ## Executive Summary This document proposes a unified `uses` keyword system for associating behaviors and schedules with characters and institutions in the Storybook DSL. The design enables: - Characters using one or more behaviors - Characters using one or more schedules - Institutions using behaviors (for institutional operations) - Institutions using schedules (operating hours, seasonal variations) - Conditional/contextual selection of linked resources - Priority-based behavior selection at runtime --- ## 1. Design Goals ### Primary Goals 1. **Unified Syntax**: Single `uses` keyword for both behaviors and schedules 2. **Simple Default Case**: Most common use case should be simple one-liner 3. **Powerful When Needed**: Support complex multi-link scenarios with conditions 4. **Clear Semantics**: Unambiguous about which behavior/schedule applies when 5. **Backward Compatible**: Existing .sb files continue to parse and work ### Non-Goals 1. **Not for Relationships**: Relationship linking remains separate (already exists) 2. **Not Inline Definitions**: Can only link to named behaviors/schedules, not define inline 3. **Not Dynamic Composition**: Links are static at author-time, selection is runtime --- ## 2. Syntax Design ### 2.1 Simple Single Link The most common case: a character has one primary behavior and one schedule. ```storybook character Martha: Human { age: 34 uses behavior: WorkAtBakery uses schedule: BakerSchedule } ``` **AST Representation:** ```rust // Add to Character struct in ast.rs pub struct Character { pub name: String, pub species: Option, pub fields: Vec, pub template: Option>, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } ``` ### 2.2 Multiple Links with Priorities Characters may have multiple behaviors that activate based on context: ```storybook character Alice: Human { age: 7 uses behaviors: [ { tree: HandleUrgentNeeds, priority: critical } { tree: CuriousExplorer, priority: normal } { tree: Idle, priority: low } ] } ``` **Semantics:** - Higher priority behaviors preempt lower priority ones - Within same priority, declaration order determines evaluation - `critical` > `high` > `normal` > `low` ### 2.3 Conditional Links Links can have `when` conditions for context-based selection: ```storybook character Alice: Human { uses behaviors: [ { tree: HandleUrgentNeeds, priority: critical } { tree: GiantBehavior, when: current_size == huge } { tree: TinyBehavior, when: current_size == tiny } { tree: NormalExploring, default: true } ] uses schedules: [ { schedule: SleepSchedule, when: emotional_state == exhausted } { schedule: AdventureSchedule, default: true } ] } ``` **Condition Evaluation:** - Conditions use the existing expression language (see design.md §5) - `default: true` means "use this if no condition matches" - Only one `default` allowed per link type - Runtime evaluates conditions top-to-bottom ### 2.4 Institution Links Institutions can link to behaviors and schedules: ```storybook institution Bakery { type: commercial uses behavior: BakeryOperations uses schedule: BakeryHours } ``` **Multiple Schedules for Seasons:** ```storybook institution Bakery { uses schedules: [ { schedule: SummerHours, when: season == summer } { schedule: WinterHours, when: season == winter } { schedule: StandardHours, default: true } ] } ``` ### 2.5 Template Inheritance Templates can define default links that characters inherit: ```storybook template WonderlandCreature { uses behavior: WonderlandBehavior uses schedule: WonderlandSchedule } character CheshireCat: Cat from WonderlandCreature { // Inherits WonderlandBehavior and WonderlandSchedule // Can override: uses behavior: CheshireBehavior // Replaces WonderlandBehavior } ``` **Override Semantics:** - If character defines `uses behavior:`, it replaces template's behavior link entirely - If character defines `uses behaviors: [...]`, it replaces template's behavior links - No merging—it's full replacement (consistent with current template override system) --- ## 3. AST Design ### 3.1 New AST Types ```rust // In src/syntax/ast.rs /// A link to a behavior tree #[derive(Debug, Clone, PartialEq)] pub struct BehaviorLink { pub tree: Vec, // Qualified path to behavior pub priority: Option, // critical, high, normal, low pub condition: Option, // when clause pub is_default: bool, // default: true pub span: Span, } /// A link to a schedule #[derive(Debug, Clone, PartialEq)] pub struct ScheduleLink { pub schedule: Vec, // Qualified path to schedule pub condition: Option, // when clause pub is_default: bool, // default: true pub span: Span, } // Priority levels (could be enum or validated string) pub enum Priority { Critical, High, Normal, Low, } ``` ### 3.2 Modified AST Structs ```rust // Character gains link fields pub struct Character { pub name: String, pub species: Option, pub fields: Vec, pub template: Option>, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } // Institution gains link fields pub struct Institution { pub name: String, pub fields: Vec, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } // Template can also have links pub struct Template { pub name: String, pub fields: Vec, pub includes: Vec>, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } ``` --- ## 4. Parser Design (LALRPOP) ### 4.1 Grammar Productions ```lalrpop // In parser.lalrpop // Character definition with optional links pub Character: Character = { "character" "{" "}" => { let mut fields = vec![]; let mut behavior_links = vec![]; let mut schedule_links = vec![]; for item in items { match item { CharacterItem::Field(f) => fields.push(f), CharacterItem::BehaviorLink(bl) => behavior_links.extend(bl), CharacterItem::ScheduleLink(sl) => schedule_links.extend(sl), } } Character { name, species, fields, template, behavior_links, schedule_links, span } } }; CharacterItem: CharacterItem = { => CharacterItem::Field(<>), => CharacterItem::BehaviorLink(<>), => CharacterItem::ScheduleLink(<>), }; // Behavior link statement BehaviorLinkStmt: Vec = { // Single link: uses behavior: BehaviorName "uses" "behavior" ":" => { vec![BehaviorLink { tree: path, priority: None, condition: None, is_default: false, span, }] }, // Multiple links: uses behaviors: [...] "uses" "behaviors" ":" "[" > "]" => links, }; BehaviorLinkSpec: BehaviorLink = { // { tree: Name, priority: normal, when: condition, default: true } "{" > "}" => { let mut tree = None; let mut priority = None; let mut condition = None; let mut is_default = false; for field in fields { match field { BehaviorLinkField::Tree(path) => tree = Some(path), BehaviorLinkField::Priority(p) => priority = Some(p), BehaviorLinkField::Condition(c) => condition = Some(c), BehaviorLinkField::Default => is_default = true, } } BehaviorLink { tree: tree.expect("tree field required"), priority, condition, is_default, span, } }, }; BehaviorLinkField: BehaviorLinkField = { "tree" ":" => BehaviorLinkField::Tree(<>), "priority" ":" => BehaviorLinkField::Priority(<>), "when" ":" => BehaviorLinkField::Condition(<>), "default" ":" "true" => BehaviorLinkField::Default, }; // Schedule link statement (parallel structure) ScheduleLinkStmt: Vec = { "uses" "schedule" ":" => { vec![ScheduleLink { schedule: path, condition: None, is_default: false, span, }] }, "uses" "schedules" ":" "[" > "]" => links, }; ScheduleLinkSpec: ScheduleLink = { "{" > "}" => { let mut schedule = None; let mut condition = None; let mut is_default = false; for field in fields { match field { ScheduleLinkField::Schedule(path) => schedule = Some(path), ScheduleLinkField::Condition(c) => condition = Some(c), ScheduleLinkField::Default => is_default = true, } } ScheduleLink { schedule: schedule.expect("schedule field required"), condition, is_default, span, } }, }; ScheduleLinkField: ScheduleLinkField = { "schedule" ":" => ScheduleLinkField::Schedule(<>), "when" ":" => ScheduleLinkField::Condition(<>), "default" ":" "true" => ScheduleLinkField::Default, }; ``` --- ## 5. Resolution & Validation ### 5.1 Name Resolution During the resolution pass (Pass 3 in design.md §4.7), the resolver must: 1. **Resolve Behavior Paths**: Each `tree: BehaviorName` must reference a valid `behavior` declaration 2. **Resolve Schedule Paths**: Each `schedule: ScheduleName` must reference a valid `schedule` declaration 3. **Validate Priorities**: Priority values must be one of {critical, high, normal, low} 4. **Validate Conditions**: Expressions in `when` clauses must be valid and type-check **Error Examples:** ``` error: unresolved behavior reference ┌─ characters/alice.sb:12:23 │ 12 │ uses behavior: CuriousExporer │ ^^^^^^^^^^^^^^ no behavior named `CuriousExporer` exists │ = help: did you mean `CuriousExplorer`? (defined in behaviors/alice_behaviors.sb) ``` ### 5.2 Semantic Validation 1. **At Most One Default**: Each link array can have at most one `default: true` 2. **Priority + Default Conflicts**: If `default: true`, priority should be `low` or omitted 3. **Condition Completeness**: Warn if conditions are not exhaustive (no default + gaps in conditions) **Warning Example:** ``` warning: conditions may not cover all cases ┌─ characters/alice.sb:8:5 │ 8 │ uses behaviors: [ 9 │ { tree: GiantBehavior, when: current_size == huge } 10 │ { tree: TinyBehavior, when: current_size == tiny } 11 │ ] │ = note: no default behavior specified and conditions don't cover all size values = help: add a default behavior: { tree: NormalBehavior, default: true } ``` ### 5.3 Template Merge Logic When a character uses `from Template`, behavior and schedule links are merged: ```rust // Pseudocode for merge logic fn merge_character_with_template(char: Character, template: Template) -> ResolvedCharacter { let behavior_links = if !char.behavior_links.is_empty() { char.behavior_links // Character overrides completely } else { template.behavior_links // Inherit from template }; let schedule_links = if !char.schedule_links.is_empty() { char.schedule_links } else { template.schedule_links }; // ... merge fields, etc. } ``` **Key Rule**: If character defines ANY behavior links, template's behavior links are ignored entirely. Same for schedules. This is all-or-nothing replacement, not merging. (no, i like merging. idk how we'll handle conflicts, but i want to support that kinda composition) --- ## 6. Resolved Type Representation ### 6.1 Resolved Link Types ```rust // In src/types.rs /// Resolved behavior link with all references resolved #[derive(Debug, Clone, PartialEq)] pub struct ResolvedBehaviorLink { pub tree_name: String, // Fully qualified behavior name pub priority: Priority, // Resolved to enum pub condition: Option, // Validated expression pub is_default: bool, } /// Resolved schedule link #[derive(Debug, Clone, PartialEq)] pub struct ResolvedScheduleLink { pub schedule_name: String, // Fully qualified schedule name pub condition: Option, pub is_default: bool, } #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Priority { Critical = 3, High = 2, Normal = 1, Low = 0, } ``` ### 6.2 Updated Resolved Structs ```rust pub struct ResolvedCharacter { pub name: String, pub species: Option, pub fields: HashMap, pub prose_blocks: HashMap, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } pub struct ResolvedInstitution { pub name: String, pub fields: HashMap, pub behavior_links: Vec, // NEW pub schedule_links: Vec, // NEW pub span: Span, } ``` --- ## 7. SBIR Representation Proposal ### 7.1 CHARACTERS Section Extension Currently, the CHARACTERS section (called ENTITIES in some specs) stores character data. We extend it: ``` CHARACTERS Section: - count: u32 - characters: [Character...] Character: - name: String - species: Option - fields: Map - behavior_links: [BehaviorLink...] <-- NEW - schedule_links: [ScheduleLink...] <-- NEW BehaviorLink: - behavior_id: u32 (index into BEHAVIORS section) - priority: u8 (0=low, 1=normal, 2=high, 3=critical) - condition: Option - is_default: bool ScheduleLink: - schedule_id: u32 (index into SCHEDULES section) - condition: Option - is_default: bool Expression: - (Existing expression bytecode format from design.md §5) ``` ### 7.2 INSTITUTIONS Section Extension ``` INSTITUTIONS Section: - count: u32 - institutions: [Institution...] Institution: - name: String - fields: Map - behavior_links: [BehaviorLink...] <-- NEW - schedule_links: [ScheduleLink...] <-- NEW ``` ### 7.3 BEHAVIORS and SCHEDULES Sections These sections remain unchanged—they define the behavior trees and schedules. Links reference them by index. **Index Resolution:** - During compilation, behavior/schedule names are resolved to their index in the respective section - At runtime, the engine uses the index to look up the behavior/schedule definition --- ## 8. Runtime Link Resolution Algorithm ### 8.1 Behavior Selection When the engine needs to select a behavior for a character: ```rust fn select_behavior(character: &Character, context: &RuntimeContext) -> Option<&Behavior> { let mut candidates: Vec<_> = character.behavior_links .iter() .filter(|link| { // Evaluate condition if present link.condition.is_none() || evaluate_condition(&link.condition, context) }) .collect(); if candidates.is_empty() { return None; } // Sort by priority (descending) candidates.sort_by(|a, b| b.priority.cmp(&a.priority)); // Return highest priority candidate // (If multiple same priority, declaration order is preserved by stable sort) let selected = candidates[0]; Some(get_behavior_by_id(selected.behavior_id)) } ``` **Key Properties:** - Priority-based selection: higher priority wins - Conditions filter candidates before priority sorting - `default: true` only matters if no conditions match (it's implicitly `when: true`) - Deterministic: same context always yields same behavior ### 8.2 Schedule Selection ```rust fn select_schedule(entity: &Entity, context: &RuntimeContext) -> Option<&Schedule> { for link in &entity.schedule_links { if link.is_default { continue; // Skip default, check it last } if link.condition.is_none() || evaluate_condition(&link.condition, context) { return Some(get_schedule_by_id(link.schedule_id)); } } // No conditions matched, use default if present entity.schedule_links .iter() .find(|link| link.is_default) .map(|link| get_schedule_by_id(link.schedule_id)) } ``` **Key Properties:** - First-match semantics: first condition that evaluates to true wins - Default is fallback: only used if no condition matches - Order matters: earlier links are checked first --- ## 9. Examples ### 9.1 Simple Character with Behavior and Schedule ```storybook behavior BakerBehavior { > { check_oven serve_customers clean_workspace } } schedule BakerSchedule { block work { 5:00 - 13:00 } block lunch { 13:00 - 14:00 } block home { 14:00 - 22:00 } block sleep { 22:00 - 5:00 } } character Martha: Human { age: 34 occupation: baker uses behavior: BakerBehavior uses schedule: BakerSchedule } ``` ### 9.2 Character with Multiple Context-Dependent Behaviors ```storybook character Alice: Human { age: 7 current_size: normal emotional_state: curious uses behaviors: [ { tree: PanicBehavior, priority: critical, when: emotional_state == frightened } { tree: GiantBehavior, when: current_size == huge } { tree: TinyBehavior, when: current_size == tiny } { tree: BraveBehavior, when: emotional_state == brave } { tree: CuriousExplorer, default: true } ] uses schedules: [ { schedule: SleepingSchedule, when: emotional_state == exhausted } { schedule: AdventureSchedule, default: true } ] } ``` ### 9.3 Institution with Seasonal Schedules ```storybook schedule SummerHours { block open { 6:00 - 20:00 } block closed { 20:00 - 6:00 } } schedule WinterHours { block open { 7:00 - 18:00 } block closed { 18:00 - 7:00 } } institution Bakery { type: commercial uses behavior: BakeryOperations uses schedules: [ { schedule: SummerHours, when: season == summer } { schedule: WinterHours, when: season == winter } ] } ``` ### 9.4 Template with Default Links ```storybook behavior WonderlandBehavior { > { speak_nonsense violate_logic } } schedule WonderlandSchedule { block awake { 0:00 - 24:00 } // Always awake in dreams } template WonderlandCreature { uses behavior: WonderlandBehavior uses schedule: WonderlandSchedule } character CheshireCat: Cat from WonderlandCreature { // Inherits WonderlandBehavior and WonderlandSchedule can_vanish: true } character Alice: Human from WonderlandCreature { // Overrides behavior but keeps schedule uses behavior: CuriousExplorer } ``` --- ## 10. Open Questions for User Review (Checkpoint 1) ### Question 1: Priority vs. Declaration Order **Current Design:** Priority determines order, then declaration order breaks ties. **Alternative:** Remove priority, use only declaration order (simpler but less expressive). **Recommendation:** Keep priority. It's more explicit and handles common use cases like "urgent needs always trump routine activities." (i guess priority is fine) ### Question 2: Condition Syntax Sugar **Current Design:** Full condition expressions. **Alternative:** Add syntactic sugar for common patterns: ```storybook uses behaviors: [ { tree: GiantBehavior, when: current_size == huge } // vs. { tree: GiantBehavior, when current_size: huge } // shorter ] ``` **Recommendation:** Start with full expressions, add sugar if usage reveals patterns. (use `==` or `is`, support both like python does.) ### Question 3: Schedule-Behavior Integration **Current Design:** Behaviors and schedules are separate links. Schedule determines WHEN, behavior determines WHAT. **Alternative:** Allow schedules to specify behaviors inline: ```storybook schedule WorkSchedule { block work { 9:00 - 17:00, behavior: WorkBehavior } } ``` **Recommendation:** Defer inline behaviors to schedule system design (Task #8). Keep linking separate for now. (yeah that's fine) ### Question 4: Link Override Semantics **Current Design:** If character defines any behavior links, template's links are completely replaced. **Alternative:** Merge character and template links (character links come first, then template links). **Recommendation:** Keep replacement semantics. It's clearer and matches existing override system. (sure? i don't remember the nuances tbh) ### Question 5: Multiple Defaults **Current Design:** At most one `default: true` per link type. **Alternative:** Allow multiple defaults with priority order. **Recommendation:** Keep single default. Multiple defaults creates ambiguity. (single default makes sense?) --- ## 11. Implementation Plan (for Task #6) ### Phase 1: AST Extension (Week 1) 1. Add `BehaviorLink` and `ScheduleLink` structs to `ast.rs` 2. Add link fields to `Character`, `Institution`, `Template` 3. Update `Declaration` enum if needed ### Phase 2: Parser Implementation (Week 1-2) 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 ### Phase 3: Resolution (Week 2) 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 ### Phase 4: Resolved Types (Week 2) 1. Add `ResolvedBehaviorLink` and `ResolvedScheduleLink` to `types.rs` 2. Update `ResolvedCharacter` and `ResolvedInstitution` 3. Implement conversion in `resolve/convert.rs` 4. Write conversion tests ### Phase 5: Validation & Diagnostics (Week 3) 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 ### Phase 6: Integration & Documentation (Week 3) 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) **Total Estimate:** 3 weeks implementation after design approval. --- ## 12. 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] Priorities supported for behaviors - [x] Default fallback supported (`default: true`) - [x] Template inheritance works - [x] Character linking works - [x] Institution linking works - [x] Parser produces correct AST - [x] Resolver validates references - [x] Clear error messages - [x] SBIR format defined ### Should Have - [ ] Warning for incomplete condition coverage - [ ] Examples for all use cases - [ ] Migration guide if needed - [ ] Runtime selection algorithm specification - [ ] Performance characteristics documented ### Nice to Have - [ ] Visual editor support design - [ ] Auto-completion for behavior/schedule names - [ ] Link refactoring tools --- ## 13. Risks & Mitigation | Risk | Likelihood | Impact | Mitigation | |------|-----------|--------|------------| | Syntax conflicts with relationship linking | Low | Medium | Different syntax context—parser can disambiguate | | Complex condition expressions hard to debug | Medium | Medium | Good error messages, warnings for non-exhaustive conditions | | Priority system confusing for users | Medium | Low | Clear documentation, examples, default priority=normal | | Template override semantics unclear | Medium | Medium | Explicit documentation, validation warnings | | SBIR encoding inefficient | Low | Low | Use indices for references, compress expressions | | Runtime selection too slow | Low | Medium | Profile early, cache selections if needed | --- ## Appendix A: Comparison with Relationship Linking **Relationship Linking** (existing): - Top-level `relationship` declarations - Participants within relationships - Bidirectional by nature - `self`/`other` blocks for asymmetry **Resource Linking** (this design): - Links within character/institution definitions - References to external behaviors/schedules - Unidirectional (character → behavior) - Conditions/priorities for selection These are complementary systems serving different purposes. No conflict. --- ## Appendix B: Grammar Sketch (Full) ```lalrpop // Simplified grammar showing link integration Character: Character = { "character" "{" "}" => // ... construct Character }; CharacterItem = { Field, BehaviorLinkStmt, ScheduleLinkStmt, ProseBlock, }; BehaviorLinkStmt: Vec = { "uses" "behavior" ":" => // single link "uses" "behaviors" ":" "[" > "]" => // multi link }; BehaviorLinkSpec: BehaviorLink = { "{" > "}" => // parse fields into BehaviorLink }; BehaviorLinkField = { "tree" ":" QualifiedPath, "priority" ":" Ident, "when" ":" Expr, "default" ":" "true", }; // Parallel structure for schedules... ``` --- **End of Design Document** **Next Step:** Present to user (Sienna) for Checkpoint 1 review and approval.