Files
storybook/design/year-long-schedule-system-revision.md

1152 lines
28 KiB
Markdown
Raw Normal View History

# 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 `modifies` 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 `modifies` 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:
```storybook
// 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
```storybook
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:
```storybook
// 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:**
- `modifies` - 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:**
```storybook
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 modifies 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:
```storybook
schedule Base {
block work { 9:00 - 17:00, action: GenericWork }
}
schedule Baker modifies 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):
```storybook
schedule Base {
block work { 9:00 - 17:00, action: GenericWork, location: Office }
}
schedule Remote modifies 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):**
```storybook
block work { 9:00 - 17:00, activity: "work" } // String
```
**New (v0.2):**
```storybook
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:**
```storybook
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:
```storybook
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
```storybook
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:**
```storybook
schedule WeeklyRoutine {
on Fireday modifies BaseWorkday {
override work { action: FireWork }
block ritual { 6:00 - 7:00, action: FireRitual }
}
}
```
### 4.2 Seasonal Patterns
```storybook
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:**
```storybook
season (Bloom, Growth) { // Both use same schedule
block outdoor_work { 5:00 - 18:00, action: FarmWork }
}
```
### 4.3 Recurrence Patterns
**Named recurring events:**
```storybook
schedule AnnualPattern modifies 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):**
```storybook
block { 9:00 - 17:00, activity: work } // Anonymous
```
**New (v0.2):**
```storybook
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:**
```storybook
block <name> { <fields> }
block <name> { <time>, <fields> }
```
**Time specification:**
```storybook
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:
```storybook
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:**
```storybook
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:**
```storybook
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):**
```storybook
block work { 9:00 - 17:00, action: Work }
```
**Long form:**
```storybook
block work {
time: 9:00 - 17:00,
action: Work
}
```
**Both valid, same result.**
---
## 8. AST Design
### 8.1 Simplified Schedule Structure
```rust
// In src/syntax/ast.rs
#[derive(Debug, Clone, PartialEq)]
pub struct Schedule {
pub name: String,
pub modifies: Option<Vec<String>>, // modifies 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 modifies: 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
```lalrpop
pub Schedule: Schedule = {
"schedule" <name:Ident> <modifies:ModifiesClause?> "{"
<items:ScheduleItem*>
"}" => Schedule { name, modifies, items, span }
};
ModifiesClause: Vec<String> = {
"modifies" <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> <modifies:ModifiesClause?> "{" <blocks:ScheduleBlock*> "}" =>
DayPattern { day, modifies, 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:
```storybook
// 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
```storybook
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:
```rust
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:**
```storybook
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)
```storybook
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
```storybook
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 modifies 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
```storybook
// 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
```storybook
// 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
```storybook
schedule BakerAnnual modifies 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
```storybook
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 modifies BaseHuman {
block work { 9:00 - 17:00, action: GenericWork }
}
schedule Baker modifies 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
```storybook
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
```storybook
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
```storybook
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> // modifies 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 modifies 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<String>)
3. Add TimeRange struct with required start/end
4. Add action field as behavior reference (Vec<String> 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 `modifies`/`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
- [x] User-defined calendars via enums
- [x] Simplified composition (`modifies`, `override` only)
- [x] Behavior references (not string activities)
- [x] Required block names
- [x] Required time ranges
- [x] Time overlap detection (warnings)
- [x] Day-specific patterns
- [x] Seasonal patterns
- [x] Recurrence patterns with `recurrence` keyword
- [x] Multi-level inheritance
- [x] 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)