922 lines
26 KiB
Markdown
922 lines
26 KiB
Markdown
|
|
# 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<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,
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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<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
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```lalrpop
|
||
|
|
// 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:
|
||
|
|
|
||
|
|
```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<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
|
||
|
|
|
||
|
|
```rust
|
||
|
|
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. 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" <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.
|