Files
storybook/design/resource-linking-checkpoint2-addendum.md
Sienna Meridian Satterwhite 16deb5d237 release: Storybook v0.2.0 - Major syntax and features update
BREAKING CHANGES:
- Relationship syntax now requires blocks for all participants
- Removed self/other perspective blocks from relationships
- Replaced 'guard' keyword with 'if' for behavior tree decorators

Language Features:
- Add tree-sitter grammar with improved if/condition disambiguation
- Add comprehensive tutorial and reference documentation
- Add SBIR v0.2.0 binary format specification
- Add resource linking system for behaviors and schedules
- Add year-long schedule patterns (day, season, recurrence)
- Add behavior tree enhancements (named nodes, decorators)

Documentation:
- Complete tutorial series (9 chapters) with baker family examples
- Complete reference documentation for all language features
- SBIR v0.2.0 specification with binary format details
- Added locations and institutions documentation

Examples:
- Convert all examples to baker family scenario
- Add comprehensive working examples

Tooling:
- Zed extension with LSP integration
- Tree-sitter grammar for syntax highlighting
- Build scripts and development tools

Version Updates:
- Main package: 0.1.0 → 0.2.0
- Tree-sitter grammar: 0.1.0 → 0.2.0
- Zed extension: 0.1.0 → 0.2.0
- Storybook editor: 0.1.0 → 0.2.0
2026-02-13 21:52:03 +00:00

18 KiB

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):

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:

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

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

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

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:

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:

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?

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

fn merge_behavior_links(
    character_links: Vec<BehaviorLink>,
    template_links: Vec<BehaviorLink>
) -> Vec<BehaviorLink> {
    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

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

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

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:

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

// 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:

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)

when: current_size is not huge      // Python style
when: current_size != huge          // C style

Recommendation: Yes, support is not for consistency with Python:

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:

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:

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

  • Unified uses keyword for behaviors and schedules
  • Single-link syntax works (uses behavior: Name)
  • Multi-link syntax works (uses behaviors: [...])
  • Conditions supported (when: expression)
  • Both == and is operators supported ← NEW
  • Both != and is not operators supported ← NEW
  • Priorities supported for behaviors
  • Default fallback supported (default: true)
  • Template merging works (concatenation + override) ← UPDATED
  • Multi-level template inheritance works ← NEW
  • Character linking works
  • Institution linking works
  • Parser produces correct AST
  • Resolver validates references
  • Warnings for merge conflicts ← NEW
  • Clear error messages
  • SBIR format defined

5. Open Questions for Sienna (Checkpoint 2)

Question 1: Empty Array Semantics ("not defined)

What should this mean?

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?

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).