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
28 KiB
Year-Long Composable Schedule System - REVISION
Author: Resource Linking Architect (Schedule System Designer) Date: 2026-02-12 Status: Revised based on User Feedback Version: 0.2 Supersedes: year-long-schedule-system.md v0.1
Changes from v0.1
This revision addresses Sienna's feedback:
- ✅ Simplified composition - Keep only
extendsandoverride, removeremove/append/replace all - ✅ User-configurable calendars - Define seasons/days as enums in schema, not hardcoded
- ✅ Behavior references - Blocks reference behaviors/actions, not string activity names
- ✅ Required block names - All blocks must be named for override system
- ✅ Time overlap detection - Validator warns on overlapping blocks
- ✅ Explicit time ranges - No default durations, must specify start/end
- ✅ Recurrence keyword - Named recurring patterns instead of
every
Executive Summary
This document proposes a year-long, composable schedule system for the Storybook DSL that:
- User-defined calendars: Seasons, days, months defined as enums (8 seasons? 5-day weeks? You decide!)
- Simple composition: Only
extendsandoverride(no complex operators) - Behavior integration: Schedule blocks reference behaviors, not string activities
- Temporal patterns: Day-specific, seasonal, and recurring event patterns
- Type-safe: Validator ensures calendar references are valid
- Backward compatible: Current simple schedules still work
1. User-Configurable Calendar System
1.1 The Problem
Original design hardcoded Euro-centric calendar:
- 4 seasons (Spring, Summer, Fall, Winter)
- 7-day week (Monday-Sunday)
- 12 Gregorian months
User need: Lonni wants 8 seasons, fantasy day names, custom time systems.
1.2 The Solution: Calendar Enums
Define calendar in schema files using existing enum system:
// In schema/calendar.sb
enum Season {
EarlySpring
LateSpring
EarlySummer
LateSummer
EarlyFall
LateFall
EarlyWinter
LateWinter
}
enum DayOfWeek {
Fireday
Waterday
Earthday
Airday
Spiritday
}
enum Month {
FirstMonth
SecondMonth
ThirdMonth
FourthMonth
FifthMonth
SixthMonth
SeventhMonth
EighthMonth
NinthMonth
TenthMonth
EleventhMonth
TwelfthMonth
}
Benefits:
- ✅ Uses existing enum system (no new concepts)
- ✅ Type-safe (validator checks references)
- ✅ Version-controlled with content
- ✅ Infinitely flexible (any calendar system)
- ✅ Self-documenting (enum values show available options)
1.3 Using Calendar Enums in Schedules
schedule FarmSchedule {
season (EarlySpring, LateSpring) {
block planting { 5:00 - 18:00, action: PlantCrops }
}
season (EarlySummer, LateSummer) {
block tending { 6:00 - 16:00, action: TendFields }
}
on Fireday {
block ritual { 6:00 - 7:00, action: FireRitual }
}
}
Validator checks:
EarlySpringexists inSeasonenum? ✓Firedayexists inDayOfWeekenum? ✓- Clear error if user typos: "Unknown season 'EarlySrping'. Did you mean 'EarlySpring'?"
1.4 Standard Calendar (Convenience)
For users who want standard calendar, we ship a default schema:
// In schema/standard_calendar.sb
enum Season {
Spring
Summer
Fall
Winter
}
enum DayOfWeek {
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
}
enum Month {
January
February
March
April
May
June
July
August
September
October
November
December
}
Users can use schema::standard_calendar::* or define their own!
2. Simplified Composition System
2.1 Only Two Operators
Keep:
extends- Inherit from base scheduleoverride- Replace specific blocks
Remove:
- Too complex, just override with empty/different blockremove- Just define new blocks directlyappend- Just don't extend if you want fresh startreplace all
2.2 Composition Examples
Basic Extension:
schedule BaseWorkday {
block sleep { 0:00 - 6:00, action: Sleep }
block work { 9:00 - 17:00, action: Work }
block leisure { 17:00 - 22:00, action: Leisure }
block sleep_late { 22:00 - 24:00, action: Sleep }
}
schedule BakerSchedule extends BaseWorkday {
override work { 5:00 - 13:00, action: BakeryWork }
block baking { 3:00 - 5:00, action: PrepBread } // New block, not override
}
Result:
sleep(0:00-6:00) - inheritedbaking(3:00-5:00) - newwork(5:00-13:00) - overriddenleisure(17:00-22:00) - inheritedsleep_late(22:00-24:00) - inherited
2.3 Override Semantics
Override by block name, not by time:
schedule Base {
block work { 9:00 - 17:00, action: GenericWork }
}
schedule Baker extends Base {
override work { 5:00 - 13:00, action: BakeryWork }
}
// Baker's 'work' block replaces Base's 'work' block entirely
// Time changed from 9:00-17:00 to 5:00-13:00
// Action changed from GenericWork to BakeryWork
Partial override (only change some fields):
schedule Base {
block work { 9:00 - 17:00, action: GenericWork, location: Office }
}
schedule Remote extends Base {
override work { location: Home } // Only change location
}
// Result: work { 9:00 - 17:00, action: GenericWork, location: Home }
3. Behavior References (Not String Activities)
3.1 The Change
Old (v0.1):
block work { 9:00 - 17:00, activity: "work" } // String
New (v0.2):
block work { 9:00 - 17:00, action: WorkBehavior } // Behavior reference
3.2 Rationale
Benefits:
- Type-safe: Validator ensures
WorkBehaviorexists - Flexible: Different schedules can use different behaviors for same time
- Composable: Behaviors are already defined elsewhere
- Clear: No ambiguity about what "work" means
Example:
behavior BakeryWork {
> {
check_oven
serve_customers
clean_workspace
}
}
behavior OfficeWork {
> {
check_email
attend_meetings
write_reports
}
}
schedule BakerSchedule {
block work { 5:00 - 13:00, action: BakeryWork }
}
schedule OfficeSchedule {
block work { 9:00 - 17:00, action: OfficeWork }
}
Both schedules have a work block, but they reference different behaviors!
3.3 Optional Fields
Blocks can still have additional fields for context:
block work {
time: 9:00 - 17:00,
action: BakeryWork,
location: Bakery,
priority: high,
notes: "Must arrive early to prep dough"
}
4. Temporal Patterns
4.1 Day-Specific Patterns
schedule WeeklyRoutine {
on Fireday {
block ritual { 6:00 - 7:00, action: FireRitual }
block work { 8:00 - 16:00, action: CraftWork }
}
on Waterday {
block meditation { 6:00 - 7:00, action: WaterMeditation }
block work { 8:00 - 16:00, action: CraftWork }
}
on Spiritday {
block rest { 0:00 - 24:00, action: Rest }
}
}
Can extend base schedule per day:
schedule WeeklyRoutine {
on Fireday extends BaseWorkday {
override work { action: FireWork }
block ritual { 6:00 - 7:00, action: FireRitual }
}
}
4.2 Seasonal Patterns
schedule FarmSchedule {
season (EarlySpring, LateSpring) {
block planting { 5:00 - 18:00, action: PlantCrops }
}
season (EarlySummer, LateSummer) {
block tending { 6:00 - 16:00, action: TendFields }
}
season (EarlyFall, LateFall) {
block harvest { 5:00 - 19:00, action: HarvestCrops }
}
season (EarlyWinter, LateWinter) {
block planning { 9:00 - 15:00, action: PlanNextYear }
}
}
Multiple seasons in one pattern:
season (Bloom, Growth) { // Both use same schedule
block outdoor_work { 5:00 - 18:00, action: FarmWork }
}
4.3 Recurrence Patterns
Named recurring events:
schedule AnnualPattern extends BaseWorkday {
recurrence MarketDay on Earthday {
block market { 8:00 - 13:00, action: SellAtMarket }
}
recurrence GuildMeeting on FirstFiredayOfMonth {
block meeting { 19:00 - 21:00, action: GuildBusiness }
}
recurrence Festival on MidsummerDay {
block celebration { 0:00 - 24:00, action: Celebrate }
}
}
Recurrence types:
on <DayOfWeek>- Every week on that dayon First<DayOfWeek>OfMonth- First occurrence each monthon Last<DayOfWeek>OfMonth- Last occurrence each monthon <SpecificDate>- Specific calendar date (e.g., Midsummer Day 15)
Why recurrence keyword:
- More explicit than
every - Can be named (useful for override/reference)
- Clear that it's a repeating pattern
- Consistent with other constructs (behavior, character, etc.)
5. Required Block Names
5.1 All Blocks Must Be Named
Old (v0.1):
block { 9:00 - 17:00, activity: work } // Anonymous
New (v0.2):
block work { 9:00 - 17:00, action: Work } // Named
5.2 Rationale
Why required:
- Override system needs names to identify blocks
- Better error messages ("overlap in 'work' block")
- Self-documenting code
- Enables referencing blocks
Syntax:
block <name> { <fields> }
block <name> { <time>, <fields> }
Time specification:
block work { 9:00 - 17:00, action: Work }
// OR
block work {
time: 9:00 - 17:00,
action: Work,
location: Office
}
6. Time Overlap Detection
6.1 Validation Rule
Validator warns when blocks overlap in same schedule:
schedule Broken {
block work { 9:00 - 17:00, action: Work }
block lunch { 12:00 - 13:00, action: Eat } // Overlaps work!
}
Warning:
warning: schedule blocks overlap
┌─ schedules/daily.sb:12:5
│
12 │ block lunch { 12:00 - 13:00, action: Eat }
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│
= note: 'lunch' (12:00-13:00) overlaps with 'work' (9:00-17:00)
= help: consider adjusting time ranges or removing one block
6.2 When Overlaps Are OK
Different days/seasons:
schedule Valid {
on Fireday {
block work { 9:00 - 17:00, action: Work }
}
on Waterday {
block work { 9:00 - 17:00, action: Work } // OK, different day
}
}
Validator only checks overlaps within same temporal context.
7. Explicit Time Ranges Required
7.1 No Default Durations
Error if time not specified:
block work { action: Work } // ERROR: missing time range
Error message:
error: missing time range for schedule block
┌─ schedules/daily.sb:5:5
│
5 │ block work { action: Work }
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
│
= note: all schedule blocks must specify a time range
= help: add time field: block work { 9:00 - 17:00, action: Work }
7.2 Time Syntax
Short form (preferred):
block work { 9:00 - 17:00, action: Work }
Long form:
block work {
time: 9:00 - 17:00,
action: Work
}
Both valid, same result.
8. AST Design
8.1 Simplified Schedule Structure
// In src/syntax/ast.rs
#[derive(Debug, Clone, PartialEq)]
pub struct Schedule {
pub name: String,
pub extends: Option<Vec<String>>, // extends BaseSchedule
pub items: Vec<ScheduleItem>, // blocks and patterns
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScheduleItem {
Block(ScheduleBlock), // Direct block definition
Override(ScheduleOverride), // override block_name { ... }
DayPattern(DayPattern), // on Fireday { ... }
SeasonPattern(SeasonPattern), // season (Bloom, Growth) { ... }
Recurrence(RecurrencePattern), // recurrence Name on ... { ... }
}
#[derive(Debug, Clone, PartialEq)]
pub struct ScheduleBlock {
pub name: String, // REQUIRED (no Option)
pub time: TimeRange, // REQUIRED
pub action: Vec<String>, // Behavior reference (qualified path)
pub fields: Vec<Field>, // Additional fields
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TimeRange {
pub start: Time, // HH:MM
pub end: Time, // HH:MM
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ScheduleOverride {
pub block_name: String,
pub fields: Vec<Field>, // Can override time, action, location, etc.
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DayPattern {
pub day: String, // Reference to DayOfWeek enum value
pub extends: Option<Vec<String>>, // Optional base schedule
pub blocks: Vec<ScheduleBlock>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SeasonPattern {
pub seasons: Vec<String>, // References to Season enum values
pub blocks: Vec<ScheduleBlock>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecurrencePattern {
pub name: String, // MarketDay, GuildMeeting
pub spec: RecurrenceSpec, // on Earthday, on FirstFiredayOfMonth
pub blocks: Vec<ScheduleBlock>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum RecurrenceSpec {
Weekly(String), // on Earthday
MonthlyFirst(String), // on FirstFiredayOfMonth
MonthlyLast(String), // on LastFiredayOfMonth
Yearly(String, u8), // on MidsummerDay 15 (month, day)
}
9. Grammar Design (LALRPOP)
9.1 Schedule Productions
pub Schedule: Schedule = {
"schedule" <name:Ident> <extends:ExtendsClause?> "{"
<items:ScheduleItem*>
"}" => Schedule { name, extends, items, span }
};
ExtendsClause: Vec<String> = {
"extends" <QualifiedPath>
};
ScheduleItem: ScheduleItem = {
"block" <ScheduleBlock> => ScheduleItem::Block(<>),
"override" <ScheduleOverride> => ScheduleItem::Override(<>),
"on" <DayPattern> => ScheduleItem::DayPattern(<>),
"season" <SeasonPattern> => ScheduleItem::SeasonPattern(<>),
"recurrence" <RecurrencePattern> => ScheduleItem::Recurrence(<>),
};
// Block: block work { 9:00 - 17:00, action: Work }
ScheduleBlock: ScheduleBlock = {
<name:Ident> "{" <fields:Comma<BlockField>> "}" => {
let mut time = None;
let mut action = None;
let mut extra_fields = vec![];
for field in fields {
match field {
BlockField::Time(t) => time = Some(t),
BlockField::Action(a) => action = Some(a),
BlockField::Other(f) => extra_fields.push(f),
}
}
ScheduleBlock {
name,
time: time.expect("time range required"),
action: action.expect("action required"),
fields: extra_fields,
span
}
}
};
BlockField = {
<TimeRange> => BlockField::Time(<>),
"action" ":" <QualifiedPath> => BlockField::Action(<>),
<Field> => BlockField::Other(<>),
};
TimeRange: TimeRange = {
<start:Time> "-" <end:Time> => TimeRange { start, end, span }
};
// Override: override work { time: 5:00 - 13:00 }
ScheduleOverride: ScheduleOverride = {
<name:Ident> "{" <fields:Comma<Field>> "}" =>
ScheduleOverride { block_name: name, fields, span }
};
// Day pattern: on Fireday { blocks }
DayPattern: DayPattern = {
<day:Ident> <extends:ExtendsClause?> "{" <blocks:ScheduleBlock*> "}" =>
DayPattern { day, extends, blocks, span }
};
// Season pattern: season (Bloom, Growth) { blocks }
SeasonPattern: SeasonPattern = {
"(" <seasons:Comma<Ident>> ")" "{" <blocks:ScheduleBlock*> "}" =>
SeasonPattern { seasons, blocks, span }
};
// Recurrence: recurrence MarketDay on Earthday { blocks }
RecurrencePattern: RecurrencePattern = {
<name:Ident> "on" <spec:RecurrenceSpec> "{" <blocks:ScheduleBlock*> "}" =>
RecurrencePattern { name, spec, blocks, span }
};
RecurrenceSpec: RecurrenceSpec = {
<Ident> => RecurrenceSpec::Weekly(<>), // on Earthday
"First" <Ident> "OfMonth" => RecurrenceSpec::MonthlyFirst(<>),
"Last" <Ident> "OfMonth" => RecurrenceSpec::MonthlyLast(<>),
<month:Ident> <day:Integer> => RecurrenceSpec::Yearly(month, day),
};
10. Resolution & Validation
10.1 Calendar Enum Validation
Validator checks that calendar references are valid enum values:
// schema/calendar.sb defines:
enum Season { Bloom, Heat, Harvest, Frost }
// In schedule:
season (Bloom, Heat) { ... } // ✓ Valid
season (Spring) { ... } // ✗ Error: 'Spring' not in Season enum
Error message:
error: unknown season
┌─ schedules/farm.sb:8:13
│
8 │ season (Spring) {
│ ^^^^^^ season 'Spring' not defined
│
= note: available seasons: Bloom, Heat, Harvest, Frost
= help: did you mean 'Bloom'?
10.2 Behavior Reference Validation
block work { 9:00 - 17:00, action: BakeryWork }
Validator checks:
- Does
BakeryWorkexist? (is it defined as a behavior?) - Is it in scope? (needs
usestatement if cross-directory)
Error if missing:
error: unresolved behavior reference
┌─ schedules/baker.sb:5:32
│
5 │ block work { 9:00 - 17:00, action: BakeryWork }
│ ^^^^^^^^^^^^^^^^^^^^^^
│
= note: no behavior named 'BakeryWork' found
= help: did you mean 'BakeryBehavior'? (in behaviors/baker.sb)
10.3 Time Overlap Detection
Validator walks blocks in same temporal context:
fn check_overlaps(blocks: &[ScheduleBlock]) -> Vec<OverlapWarning> {
let mut warnings = vec![];
for i in 0..blocks.len() {
for j in (i+1)..blocks.len() {
if blocks[i].time.overlaps(&blocks[j].time) {
warnings.push(OverlapWarning {
block1: blocks[i].name.clone(),
block2: blocks[j].name.clone(),
time1: blocks[i].time,
time2: blocks[j].time,
});
}
}
}
warnings
}
Handles midnight wraparound:
block evening { 22:00 - 24:00, action: Sleep }
block morning { 0:00 - 6:00, action: Sleep }
// No overlap - different calendar days
11. Examples
Example 1: Simple Daily Schedule (Backward Compatible)
schedule SimpleDay {
block sleep { 0:00 - 6:00, action: Sleep }
block work { 9:00 - 17:00, action: Work }
block leisure { 17:00 - 22:00, action: Leisure }
block sleep_late { 22:00 - 24:00, action: Sleep }
}
Example 2: Schedule with Extension and Override
schedule BaseWorkday {
block sleep { 0:00 - 6:00, action: Sleep }
block work { 9:00 - 17:00, action: GenericWork }
block leisure { 17:00 - 22:00, action: Leisure }
block sleep_late { 22:00 - 24:00, action: Sleep }
}
schedule BakerSchedule extends BaseWorkday {
override work { 5:00 - 13:00, action: BakeryWork }
block prep { 3:00 - 5:00, action: PrepBread }
}
// Result for Baker:
// sleep (0:00-6:00) - inherited
// prep (3:00-5:00) - new
// work (5:00-13:00) - overridden (was 9:00-17:00)
// leisure (17:00-22:00) - inherited
// sleep_late (22:00-24:00) - inherited
Example 3: Custom 8-Season Calendar
// schema/calendar.sb
enum Season {
EarlySpring
LateSpring
EarlySummer
LateSummer
EarlyFall
LateFall
EarlyWinter
LateWinter
}
// schedules/farm.sb
schedule FarmSchedule {
season (EarlySpring) {
block soil_prep { 5:00 - 18:00, action: PrepareSoil }
}
season (LateSpring) {
block planting { 5:00 - 18:00, action: PlantSeeds }
}
season (EarlySummer, LateSummer) {
block tending { 6:00 - 16:00, action: TendCrops }
}
season (EarlyFall) {
block early_harvest { 5:00 - 19:00, action: HarvestEarlyCrops }
}
season (LateFall) {
block late_harvest { 6:00 - 18:00, action: HarvestLateCrops }
}
season (EarlyWinter, LateWinter) {
block planning { 9:00 - 15:00, action: PlanNextYear }
}
}
Example 4: Fantasy 5-Day Week
// schema/calendar.sb
enum DayOfWeek {
Fireday
Waterday
Earthday
Airday
Spiritday
}
// schedules/weekly.sb
schedule MageSchedule {
on Fireday {
block fire_ritual { 6:00 - 7:00, action: FireMagic }
block research { 8:00 - 16:00, action: Research }
}
on Waterday {
block water_ritual { 6:00 - 7:00, action: WaterMagic }
block research { 8:00 - 16:00, action: Research }
}
on Earthday {
block earth_ritual { 6:00 - 7:00, action: EarthMagic }
block market { 8:00 - 13:00, action: SellPotions }
}
on Airday {
block air_ritual { 6:00 - 7:00, action: AirMagic }
block research { 8:00 - 16:00, action: Research }
}
on Spiritday {
block meditation { 0:00 - 24:00, action: SpiritMeditation }
}
}
Example 5: Recurring Market Days
schedule BakerAnnual extends BaseWorkday {
recurrence MarketDay on Earthday {
block market { 8:00 - 13:00, action: SellBread }
}
recurrence GuildMeeting on FirstFiredayOfMonth {
block meeting { 19:00 - 21:00, action: BakersGuild }
}
recurrence MidsummerFestival on Midsummer 15 {
block celebration { 0:00 - 24:00, action: Celebrate }
}
}
Example 6: Multi-Level Inheritance
schedule BaseHuman {
block sleep { 0:00 - 6:00, action: Sleep }
block breakfast { 6:00 - 7:00, action: Eat }
block dinner { 18:00 - 19:00, action: Eat }
block sleep_late { 22:00 - 24:00, action: Sleep }
}
schedule Worker extends BaseHuman {
block work { 9:00 - 17:00, action: GenericWork }
}
schedule Baker extends Worker {
override work { 5:00 - 13:00, action: BakeryWork }
override breakfast { 13:30 - 14:00, action: Eat } // Late breakfast after work
block prep { 3:00 - 5:00, action: PrepBread }
}
// Result for Baker:
// sleep (0:00-6:00) from BaseHuman
// prep (3:00-5:00) from Baker
// work (5:00-13:00) from Baker (overridden)
// breakfast (13:30-14:00) from Baker (overridden)
// dinner (18:00-19:00) from BaseHuman
// sleep_late (22:00-24:00) from BaseHuman
12. Integration with Resource Linking
12.1 Simple Case
schedule BakerSchedule {
block work { 5:00 - 13:00, action: BakeryWork }
block lunch { 13:00 - 14:00, action: Eat }
block rest { 14:00 - 22:00, action: Rest }
}
character Martha: Human {
uses schedule: BakerSchedule
}
12.2 Conditional Schedule Linking
character Martha: Human {
uses schedules: [
{ schedule: WorkdaySchedule, when: day_of_week is workday }
{ schedule: WeekendSchedule, when: day_of_week is restday }
]
}
Note: workday and restday would need to be defined as enum values or predicates.
12.3 Seasonal Institution Hours
institution Bakery {
uses schedules: [
{ schedule: SummerHours, when: season is EarlySummer or season is LateSummer }
{ schedule: WinterHours, when: season is EarlyWinter or season is LateWinter }
{ schedule: StandardHours, default: true }
]
}
13. SBIR Representation
13.1 SCHEDULES Section
SCHEDULES Section:
- count: u32
- schedules: [Schedule...]
Schedule:
- name: String
- parent_schedule_id: Option<u32> // extends reference
- blocks: [ScheduleBlock...]
- patterns: [SchedulePattern...]
ScheduleBlock:
- name: String // REQUIRED
- start: u16 // Minutes since midnight (0-1439)
- end: u16 // Minutes since midnight (0-1439)
- action_id: u32 // Index into BEHAVIORS section
- fields: Map<String, Value>
SchedulePattern:
- kind: PatternKind
- specification: bytes
- blocks: [ScheduleBlock...]
PatternKind:
- Day(day_enum_value: String)
- Season(season_enum_values: Vec<String>)
- Recurrence(spec: RecurrenceSpec)
13.2 Runtime Evaluation
At runtime, engine:
- Resolves character's schedule links (conditional selection from resource linking)
- Merges inherited schedules (parent → child)
- Evaluates patterns for current day/season
- Produces final 24-hour schedule for today
- Returns ordered list of (time, action) pairs
Example:
Character: Martha
uses schedule: BakerSchedule
BakerSchedule extends BaseWorkday
Merge result for today (Fireday, EarlySummer):
03:00-05:00 → PrepBread
05:00-13:00 → BakeryWork
13:30-14:00 → Eat
14:00-22:00 → Rest
22:00-24:00 → Sleep
00:00-06:00 → Sleep
14. Implementation Plan
Phase 1: AST Extension (Week 1)
- Simplify ScheduleItem enum (Block, Override, patterns only)
- Make ScheduleBlock.name required (String, not Option)
- Add TimeRange struct with required start/end
- Add action field as behavior reference (Vec for qualified path)
- Add DayPattern, SeasonPattern, RecurrencePattern
- Update parser to require block names and time ranges
Phase 2: Calendar Enum System (Week 1)
- Document how to define Season/DayOfWeek/Month enums
- Create standard_calendar.sb schema
- Update validator to check enum references in patterns
- Add fuzzy matching for calendar terms
- Write tests for custom calendars
Phase 3: Parser Implementation (Week 1-2)
- Implement simplified
extends/overrideparsing - Implement
on <Day>patterns - Implement
season (<Seasons>)patterns - Implement
recurrence <Name> on <Spec>patterns - Add time range validation (error if missing)
- Write parser tests
Phase 4: Resolution & Validation (Week 2)
- Implement schedule inheritance resolution
- Implement merge algorithm (same as behavior merging)
- Implement behavior reference validation
- Implement calendar enum validation
- Implement time overlap detection (warnings)
- Write resolution tests
Phase 5: Integration (Week 2-3)
- Update schedule linking in resource linking system
- Implement runtime schedule evaluation
- Update SBIR format
- Write integration tests
- Test with custom calendars (8 seasons, 5-day week)
Phase 6: Documentation & Examples (Week 3)
- Document calendar enum system
- Create examples for standard calendar
- Create examples for fantasy calendars
- Update language documentation
- Create migration guide
Total Estimate: 3 weeks
15. Success Criteria
Must Have
- User-defined calendars via enums
- Simplified composition (
extends,overrideonly) - Behavior references (not string activities)
- Required block names
- Required time ranges
- Time overlap detection (warnings)
- Day-specific patterns
- Seasonal patterns
- Recurrence patterns with
recurrencekeyword - Multi-level inheritance
- Integration with resource linking
Should Have
- Standard calendar schema (convenience)
- Clear calendar documentation
- Examples with 8-season calendar
- Examples with fantasy day names
- Comprehensive validator error messages
Nice to Have
- Month-based patterns
- Year-long pattern visualization
- Schedule conflict resolution helpers
- Visual schedule editor design
16. Open Questions (Answered)
Q1: Block Naming
Answer: ✅ Yes - all blocks must have names
Q2: Time Overlap Detection
Answer: ✅ Yes - add warnings (not errors)
Q3: Default Block Duration
Answer: ✅ Must throw error - require explicit times
Q4: Seasonal Calendar
Answer: ✅ Configurable per world via enums
Q5: Recurrence Syntax
Answer: ✅ Use recurrence keyword (more explicit than every)
End of Revised Design Document
Status: Ready for implementation after user approval Next Step: Proceed to Task #12 (implementation)