Files
storybook/design/year-long-schedule-system-revision.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

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:

  1. Simplified composition - Keep only extends and override, remove remove/append/replace all
  2. User-configurable calendars - Define seasons/days as enums in schema, not hardcoded
  3. Behavior references - Blocks reference behaviors/actions, not string activity names
  4. Required block names - All blocks must be named for override system
  5. Time overlap detection - Validator warns on overlapping blocks
  6. Explicit time ranges - No default durations, must specify start/end
  7. 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 extends and override (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:

  • EarlySpring exists in Season enum? ✓
  • Fireday exists in DayOfWeek enum? ✓
  • 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 schedule
  • override - Replace specific blocks

Remove:

  • remove - Too complex, just override with empty/different block
  • append - Just define new blocks directly
  • replace all - Just don't extend if you want fresh start

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) - inherited
  • baking (3:00-5:00) - new
  • work (5:00-13:00) - overridden
  • leisure (17:00-22:00) - inherited
  • sleep_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 WorkBehavior exists
  • 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 day
  • on First<DayOfWeek>OfMonth - First occurrence each month
  • on Last<DayOfWeek>OfMonth - Last occurrence each month
  • on <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:

  1. Does BakeryWork exist? (is it defined as a behavior?)
  2. Is it in scope? (needs use statement 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:

  1. Resolves character's schedule links (conditional selection from resource linking)
  2. Merges inherited schedules (parent → child)
  3. Evaluates patterns for current day/season
  4. Produces final 24-hour schedule for today
  5. 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)

  1. Simplify ScheduleItem enum (Block, Override, patterns only)
  2. Make ScheduleBlock.name required (String, not Option)
  3. Add TimeRange struct with required start/end
  4. Add action field as behavior reference (Vec for qualified path)
  5. Add DayPattern, SeasonPattern, RecurrencePattern
  6. Update parser to require block names and time ranges

Phase 2: Calendar Enum System (Week 1)

  1. Document how to define Season/DayOfWeek/Month enums
  2. Create standard_calendar.sb schema
  3. Update validator to check enum references in patterns
  4. Add fuzzy matching for calendar terms
  5. Write tests for custom calendars

Phase 3: Parser Implementation (Week 1-2)

  1. Implement simplified extends/override parsing
  2. Implement on <Day> patterns
  3. Implement season (<Seasons>) patterns
  4. Implement recurrence <Name> on <Spec> patterns
  5. Add time range validation (error if missing)
  6. Write parser tests

Phase 4: Resolution & Validation (Week 2)

  1. Implement schedule inheritance resolution
  2. Implement merge algorithm (same as behavior merging)
  3. Implement behavior reference validation
  4. Implement calendar enum validation
  5. Implement time overlap detection (warnings)
  6. Write resolution tests

Phase 5: Integration (Week 2-3)

  1. Update schedule linking in resource linking system
  2. Implement runtime schedule evaluation
  3. Update SBIR format
  4. Write integration tests
  5. Test with custom calendars (8 seasons, 5-day week)

Phase 6: Documentation & Examples (Week 3)

  1. Document calendar enum system
  2. Create examples for standard calendar
  3. Create examples for fantasy calendars
  4. Update language documentation
  5. Create migration guide

Total Estimate: 3 weeks


15. Success Criteria

Must Have

  • User-defined calendars via enums
  • Simplified composition (extends, override only)
  • Behavior references (not string activities)
  • Required block names
  • Required time ranges
  • Time overlap detection (warnings)
  • Day-specific patterns
  • Seasonal patterns
  • Recurrence patterns with recurrence keyword
  • 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)