Files
storybook/docs/reference/14-schedules.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

16 KiB

Schedules

Schedules define time-based routines for characters and institutions. They specify what activities occur during specific time ranges, support seasonal variations, recurring events, and template composition. Schedules enable rich temporal behavior from simple daily routines to complex year-long patterns.

What is a Schedule?

A schedule is a collection of time blocks that define activities throughout a day, week, season, or year:

  • Blocks: Time ranges (e.g., 06:00 - 14:00) with associated activities/behaviors
  • Temporal constraints: When blocks apply (season, day of week, month)
  • Recurrence patterns: Repeating events (e.g., "Market Day every Earthday")
  • Composition: Schedules can extend and override other schedules
Schedule: BakerySchedule
├─ Block: 06:00 - 08:00 → prepare_dough
├─ Block: 08:00 - 14:00 → serve_customers
├─ Recurrence: Market Day (on Earthday) → special_market_hours
└─ Extends: BaseBusiness (inherits closing hours)

Syntax

<schedule-decl> ::= "schedule" <identifier> <extends-clause>? <body>

<extends-clause> ::= "extends" <identifier>

<body> ::= "{" <schedule-item>* "}"

<schedule-item> ::= <schedule-block>
                  | <recurrence-pattern>

<schedule-block> ::= "block" <block-name>? "{" <block-content>+ "}"

<block-name> ::= <identifier>

<block-content> ::= <time-range>
                  | "action" ":" <qualified-path>
                  | <temporal-constraint>
                  | <field>

<time-range> ::= <time> "-" <time>

<temporal-constraint> ::= "on" <temporal-spec>

<temporal-spec> ::= "season" <identifier>
                  | "day" <identifier>
                  | "month" <identifier>
                  | "dates" <string> ".." <string>

<recurrence-pattern> ::= "recurs" <identifier> <temporal-constraint> "{" <schedule-block>+ "}"

Schedule Blocks

A schedule block defines an activity during a specific time range.

Basic Block

schedule SimpleBaker {
    block {
        06:00 - 14:00
        action: baking::prepare_and_sell
    }
}

Named Block

Named blocks support the override system:

schedule BakeryBase {
    block work {
        09:00 - 17:00
        action: baking::standard_work
    }
}

schedule EarlyBaker extends BakeryBase {
    block work {  // Override by name
        05:00 - 13:00
        action: baking::early_shift
    }
}

Block Content

Blocks can contain:

  1. Time range (required): HH:MM - HH:MM
  2. Action (optional): Behavior tree reference
  3. Temporal constraint (optional): When the block applies
  4. Fields (optional): Additional metadata
schedule CompleteBlock {
    block morning_work {
        06:00 - 12:00
        action: work::morning_routine
        on season summer
        location: "Outdoor Market"
    }
}

Time Ranges

Time ranges use 24-hour clock format.

Syntax

HH:MM - HH:MM
  • HH: Hour (00-23)
  • MM: Minute (00-59)
  • Optional seconds: HH:MM:SS - HH:MM:SS

Examples

schedule Examples {
    block early { 05:00 - 08:00, action: prep }
    block midday { 12:00 - 13:00, action: lunch }
    block late { 20:00 - 23:59, action: closing }
}

Overnight Ranges

Blocks can span midnight:

schedule NightShift {
    block night_work {
        22:00 - 06:00  // 10 PM to 6 AM next day
        action: security::patrol
    }
}

The system interprets this as 22:00-24:00 (day 1) + 00:00-06:00 (day 2).

Actions

The action field links a schedule block to a behavior tree.

Syntax

action: <qualified-path>

Examples

schedule BakerSchedule {
    block morning {
        05:00 - 08:00
        action: baking::prepare_dough
    }

    block sales {
        08:00 - 14:00
        action: baking::serve_customers
    }

    block cleanup {
        14:00 - 15:00
        action: baking::close_shop
    }
}

Qualified Paths

Actions can reference behaviors from other modules:

schedule GuardSchedule {
    block patrol {
        08:00 - 16:00
        action: behaviors::guards::patrol_route
    }

    block training {
        16:00 - 18:00
        action: combat::training::drills
    }
}

Temporal Constraints

Temporal constraints specify when a block applies (season, day of week, month, date range).

Season Constraint

schedule SeasonalHours {
    block summer_hours {
        06:00 - 20:00
        action: work::long_day
        on season summer
    }

    block winter_hours {
        07:00 - 18:00
        action: work::short_day
        on season winter
    }
}

Season values are defined by enums in your storybook:

enum Season {
    spring
    summer
    fall
    winter
}

Day of Week Constraint

schedule WeeklyPattern {
    block weekday_work {
        09:00 - 17:00
        action: work::standard
        on day monday  // Also: tuesday, wednesday, thursday, friday, saturday, sunday
    }

    block weekend_rest {
        10:00 - 16:00
        action: leisure::relax
        on day saturday
    }
}

Day values are typically defined by a DayOfWeek enum:

enum DayOfWeek {
    monday
    tuesday
    wednesday
    thursday
    friday
    saturday
    sunday
}

Month Constraint

schedule AnnualPattern {
    block harvest {
        06:00 - 20:00
        action: farming::harvest_crops
        on month october
    }

    block spring_planting {
        07:00 - 19:00
        action: farming::plant_seeds
        on month april
    }
}

Date Range Constraint

schedule TouristSeason {
    block high_season {
        08:00 - 22:00
        action: tourism::busy_service
        on dates "Jun 1" .. "Sep 1"
    }
}

Note: Date format is implementation-specific. Check your runtime for supported formats.

Recurrence Patterns

Recurrence patterns define recurring events that modify the schedule.

Syntax

recurs EventName on <temporal-constraint> {
    <schedule-block>+
}

Examples

Weekly Recurring Event

schedule MarketSchedule {
    // Regular daily hours
    block work {
        08:00 - 17:00
        action: shop::regular_sales
    }

    // Market day every saturday
    recurs MarketDay on day saturday {
        block setup {
            06:00 - 08:00
            action: market::setup_stall
        }

        block busy_market {
            08:00 - 18:00
            action: market::busy_sales
        }

        block teardown {
            18:00 - 20:00
            action: market::pack_up
        }
    }
}

Monthly Recurring Event

schedule TownSchedule {
    recurs CouncilMeeting on month first_monday {
        block meeting {
            18:00 - 21:00
            action: governance::council_session
        }
    }
}

Seasonal Recurring Event

schedule FarmSchedule {
    recurs SpringPlanting on season spring {
        block planting {
            06:00 - 18:00
            action: farming::plant_all_fields
        }
    }

    recurs FallHarvest on season fall {
        block harvest {
            06:00 - 20:00
            action: farming::harvest_all_crops
        }
    }
}

Schedule Composition

Schedules can extend other schedules using extends, inheriting and overriding blocks.

Extends Clause

schedule Base {
    block work {
        09:00 - 17:00
        action: work::standard
    }
}

schedule Extended extends Base {
    block work {  // Override inherited block
        05:00 - 13:00
        action: work::early_shift
    }
}

Override Semantics

When extending a schedule:

  1. Named blocks with matching names override base blocks
  2. Unnamed blocks are appended
  3. Time range can change
  4. Action can change
  5. Constraints can be added/changed

Multiple Levels

schedule BaseWork {
    block work {
        09:00 - 17:00
        action: work::standard
    }
}

schedule BakerWork extends BaseWork {
    block work {
        05:00 - 13:00  // Earlier hours
        action: baking::work
    }
}

schedule MasterBaker extends BakerWork {
    block work {
        03:00 - 11:00  // Even earlier!
        action: baking::master_work
    }

    block teaching {
        14:00 - 16:00
        action: baking::teach_apprentice
    }
}

Resolution:

  • work block: 03:00-11:00 with baking::master_work (MasterBaker overrides BakerWork overrides BaseWork)
  • teaching block: 14:00-16:00 with baking::teach_apprentice (from MasterBaker)

Complete Examples

Simple Daily Schedule

schedule SimpleFarmer {
    block sleep {
        22:00 - 05:00
        action: rest::sleep
    }

    block morning_chores {
        05:00 - 08:00
        action: farming::feed_animals
    }

    block fieldwork {
        08:00 - 17:00
        action: farming::tend_crops
    }

    block evening_chores {
        17:00 - 19:00
        action: farming::evening_tasks
    }

    block leisure {
        19:00 - 22:00
        action: social::family_time
    }
}

Seasonal Variation

schedule SeasonalBaker {
    block summer_work {
        06:00 - 20:00
        action: baking::long_shift
        on season summer
    }

    block winter_work {
        07:00 - 18:00
        action: baking::short_shift
        on season winter
    }

    block spring_work {
        06:30 - 19:00
        action: baking::medium_shift
        on season spring
    }

    block fall_work {
        06:30 - 19:00
        action: baking::medium_shift
        on season fall
    }
}

Weekly Pattern with Recurrence

schedule InnkeeperSchedule {
    // Weekday routine
    block weekday_service {
        08:00 - 22:00
        action: inn::regular_service
        on day monday  // Repeat for each weekday
    }

    block weekday_service {
        08:00 - 22:00
        action: inn::regular_service
        on day tuesday
    }

    // ... (wednesday, thursday, friday)

    // Weekend hours
    block weekend_service {
        10:00 - 24:00
        action: inn::busy_service
        on day saturday
    }

    block weekend_service {
        10:00 - 24:00
        action: inn::busy_service
        on day sunday
    }

    // Weekly market day
    recurs MarketDay on day wednesday {
        block market_prep {
            06:00 - 08:00
            action: inn::prepare_market_goods
        }

        block market_rush {
            08:00 - 16:00
            action: inn::market_day_chaos
        }
    }
}

Extended Schedule

schedule BaseShopkeeper {
    block open {
        09:00 - 17:00
        action: shop::standard_hours
    }
}

schedule BlacksmithSchedule extends BaseShopkeeper {
    block open {  // Override
        06:00 - 18:00  // Longer hours
        action: smithing::work
    }

    block forge_heat {  // New block
        05:00 - 06:00
        action: smithing::heat_forge
    }
}

Complex Year-Round Schedule

schedule MasterBakerYear {
    // Daily base pattern
    block prep {
        04:00 - 06:00
        action: baking::prepare
    }

    block baking {
        06:00 - 10:00
        action: baking::bake
    }

    block sales {
        10:00 - 16:00
        action: baking::serve
    }

    block cleanup {
        16:00 - 17:00
        action: baking::clean
    }

    // Seasonal variations
    block summer_hours {
        10:00 - 20:00  // Extended sales
        action: baking::busy_summer
        on season summer
    }

    // Weekly market
    recurs MarketDay on day saturday {
        block market_prep {
            02:00 - 04:00
            action: baking::market_prep
        }

        block market_sales {
            08:00 - 18:00
            action: baking::market_rush
        }
    }

    // Annual events
    recurs HarvestFestival on dates "Sep 20" .. "Sep 25" {
        block festival {
            06:00 - 23:00
            action: baking::festival_mode
        }
    }
}

Integration with Characters

Characters link to schedules using the uses schedule clause:

character Martha {
    uses schedule: BakerySchedule
}

Multiple schedules:

character Innkeeper {
    uses schedules: [WeekdaySchedule, WeekendSchedule]
}

See Characters for complete integration syntax.

Execution Semantics

Time-Based Selection

At any given time, the runtime:

  1. Evaluates temporal constraints: Which blocks apply right now?
  2. Selects matching block: First block whose time range and constraint match
  3. Executes action: Runs the associated behavior tree
  4. Repeats next tick

Priority Order

When multiple blocks could apply:

  1. Recurrences take priority over regular blocks
  2. Constraints filter blocks (season, day, month)
  3. Time ranges must overlap current time
  4. First match wins (declaration order)

Block Overlap

Blocks can overlap in time. The runtime selects the first matching block.

Example:

schedule Overlapping {
    block general {
        08:00 - 17:00
        action: work::general
    }

    block specialized {
        10:00 - 12:00  // Overlaps with general
        action: work::specialized
        on day wednesday
    }
}

On Wednesday at 11:00, specialized block is selected (more specific constraint).

Validation Rules

  1. Time format: Times must be valid HH:MM or HH:MM:SS (24-hour)
  2. Time order: Start time must be before end time (unless spanning midnight)
  3. Extends exists: If using extends, base schedule must be defined
  4. No circular extends: Cannot form circular dependency chains
  5. Named blocks unique: Block names must be unique within a schedule
  6. Action exists: Action references must resolve to defined behaviors
  7. Constraint values: Temporal constraint values must reference defined enums
  8. Recurrence names unique: Recurrence names must be unique within a schedule

Best Practices

1. Use Named Blocks for Overrides

Avoid:

schedule Extended extends Base {
    block { 05:00 - 13:00, action: work }  // Appends instead of overriding
}

Prefer:

schedule Extended extends Base {
    block work { 05:00 - 13:00, action: early_work }  // Overrides by name
}
schedule DailyRoutine {
    // Sleep blocks
    block night_sleep { 22:00 - 05:00, action: sleep }

    // Morning blocks
    block wake_up { 05:00 - 06:00, action: morning_routine }
    block breakfast { 06:00 - 07:00, action: eat_breakfast }

    // Work blocks
    block commute { 07:00 - 08:00, action: travel_to_work }
    block work { 08:00 - 17:00, action: work }
}

3. Use Recurrences for Special Events

Avoid: Duplicating blocks manually

Prefer:

recurs MarketDay on day saturday {
    block market { 08:00 - 16:00, action: market_work }
}

4. Extend for Variations

Avoid: Duplicating entire schedules

Prefer:

schedule WorkerBase { ... }
schedule EarlyWorker extends WorkerBase { ... }
schedule NightWorker extends WorkerBase { ... }

5. Use Temporal Constraints for Clarity

block summer_hours {
    06:00 - 20:00
    action: long_shift
    on season summer  // Explicit and readable
}

Cross-References

  • Time-based AI: Schedules drive time-dependent behavior
  • Template inheritance: extends enables schedule reuse
  • Temporal constraints: Filter blocks by season, day, month
  • Recurrence patterns: Define repeating events
  • Composition: Build complex schedules from simple parts