Files
storybook/design/resource-linking-system.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

26 KiB

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

The most common case: a character has one primary behavior and one schedule.

character Martha: Human {
    age: 34

    uses behavior: WorkAtBakery
    uses schedule: BakerSchedule
}

AST Representation:

// Add to Character struct in ast.rs
pub struct Character {
    pub name: String,
    pub species: Option<String>,
    pub fields: Vec<Field>,
    pub template: Option<Vec<String>>,
    pub behavior_links: Vec<BehaviorLink>,  // NEW
    pub schedule_links: Vec<ScheduleLink>,  // NEW
    pub span: Span,
}

Characters may have multiple behaviors that activate based on context:

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

Links can have when conditions for context-based selection:

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

Institutions can link to behaviors and schedules:

institution Bakery {
    type: commercial

    uses behavior: BakeryOperations
    uses schedule: BakeryHours
}

Multiple Schedules for Seasons:

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:

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

// In src/syntax/ast.rs

/// A link to a behavior tree
#[derive(Debug, Clone, PartialEq)]
pub struct BehaviorLink {
    pub tree: Vec<String>,        // Qualified path to behavior
    pub priority: Option<String>, // critical, high, normal, low
    pub condition: Option<Expr>,  // 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<String>,    // Qualified path to schedule
    pub condition: Option<Expr>,  // 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

// Character gains link fields
pub struct Character {
    pub name: String,
    pub species: Option<String>,
    pub fields: Vec<Field>,
    pub template: Option<Vec<String>>,
    pub behavior_links: Vec<BehaviorLink>,  // NEW
    pub schedule_links: Vec<ScheduleLink>,  // NEW
    pub span: Span,
}

// Institution gains link fields
pub struct Institution {
    pub name: String,
    pub fields: Vec<Field>,
    pub behavior_links: Vec<BehaviorLink>,  // NEW
    pub schedule_links: Vec<ScheduleLink>,  // NEW
    pub span: Span,
}

// Template can also have links
pub struct Template {
    pub name: String,
    pub fields: Vec<Field>,
    pub includes: Vec<Vec<String>>,
    pub behavior_links: Vec<BehaviorLink>,  // NEW
    pub schedule_links: Vec<ScheduleLink>,  // NEW
    pub span: Span,
}

4. Parser Design (LALRPOP)

4.1 Grammar Productions

// In parser.lalrpop

// Character definition with optional links
pub Character: Character = {
    "character" <name:Ident> <species:SpeciesClause?> <template:TemplateClause?> "{"
        <items:CharacterItem*>
    "}" => {
        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 = {
    <Field> => CharacterItem::Field(<>),
    <BehaviorLinkStmt> => CharacterItem::BehaviorLink(<>),
    <ScheduleLinkStmt> => CharacterItem::ScheduleLink(<>),
};

// Behavior link statement
BehaviorLinkStmt: Vec<BehaviorLink> = {
    // Single link: uses behavior: BehaviorName
    "uses" "behavior" ":" <path:QualifiedPath> => {
        vec![BehaviorLink {
            tree: path,
            priority: None,
            condition: None,
            is_default: false,
            span,
        }]
    },

    // Multiple links: uses behaviors: [...]
    "uses" "behaviors" ":" "[" <links:Comma<BehaviorLinkSpec>> "]" => links,
};

BehaviorLinkSpec: BehaviorLink = {
    // { tree: Name, priority: normal, when: condition, default: true }
    "{" <fields:Comma<BehaviorLinkField>> "}" => {
        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" ":" <QualifiedPath> => BehaviorLinkField::Tree(<>),
    "priority" ":" <Ident> => BehaviorLinkField::Priority(<>),
    "when" ":" <Expr> => BehaviorLinkField::Condition(<>),
    "default" ":" "true" => BehaviorLinkField::Default,
};

// Schedule link statement (parallel structure)
ScheduleLinkStmt: Vec<ScheduleLink> = {
    "uses" "schedule" ":" <path:QualifiedPath> => {
        vec![ScheduleLink {
            schedule: path,
            condition: None,
            is_default: false,
            span,
        }]
    },

    "uses" "schedules" ":" "[" <links:Comma<ScheduleLinkSpec>> "]" => links,
};

ScheduleLinkSpec: ScheduleLink = {
    "{" <fields:Comma<ScheduleLinkField>> "}" => {
        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" ":" <QualifiedPath> => ScheduleLinkField::Schedule(<>),
    "when" ":" <Expr> => 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:

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

// 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<Expr>,        // 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<Expr>,
    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

pub struct ResolvedCharacter {
    pub name: String,
    pub species: Option<String>,
    pub fields: HashMap<String, Value>,
    pub prose_blocks: HashMap<String, ProseBlock>,
    pub behavior_links: Vec<ResolvedBehaviorLink>,  // NEW
    pub schedule_links: Vec<ResolvedScheduleLink>,  // NEW
    pub span: Span,
}

pub struct ResolvedInstitution {
    pub name: String,
    pub fields: HashMap<String, Value>,
    pub behavior_links: Vec<ResolvedBehaviorLink>,  // NEW
    pub schedule_links: Vec<ResolvedScheduleLink>,  // 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<String>
  - fields: Map<String, Value>
  - 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<Expression>
  - is_default: bool

ScheduleLink:
  - schedule_id: u32                   (index into SCHEDULES section)
  - condition: Option<Expression>
  - 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<String, Value>
  - 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.1 Behavior Selection

When the engine needs to select a behavior for a character:

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

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

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

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

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 }
    ]
}
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:

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:

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)

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

  • Unified uses keyword for behaviors and schedules
  • Single-link syntax works (uses behavior: Name)
  • Multi-link syntax works (uses behaviors: [...])
  • Conditions supported (when: expression)
  • Priorities supported for behaviors
  • Default fallback supported (default: true)
  • Template inheritance works
  • Character linking works
  • Institution linking works
  • Parser produces correct AST
  • Resolver validates references
  • Clear error messages
  • 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)

// Simplified grammar showing link integration

Character: Character = {
    "character" <name:Ident> <species:SpeciesClause?> <template:TemplateClause?>
    "{" <items:CharacterItem*> "}" => // ... construct Character
};

CharacterItem = {
    Field,
    BehaviorLinkStmt,
    ScheduleLinkStmt,
    ProseBlock,
};

BehaviorLinkStmt: Vec<BehaviorLink> = {
    "uses" "behavior" ":" <QualifiedPath> => // single link
    "uses" "behaviors" ":" "[" <Comma<BehaviorLinkSpec>> "]" => // multi link
};

BehaviorLinkSpec: BehaviorLink = {
    "{" <Comma<BehaviorLinkField>> "}" => // 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.