Files
storybook/design/resource-linking-system.md

922 lines
26 KiB
Markdown
Raw Normal View History

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