2026-02-13 21:52:03 +00:00
|
|
|
# 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:
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
1. ✅ **Simplified composition** - Keep only `modifies` and `override`, remove `remove`/`append`/`replace all`
|
2026-02-13 21:52:03 +00:00
|
|
|
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!)
|
2026-02-16 22:55:04 +00:00
|
|
|
- **Simple composition**: Only `modifies` and `override` (no complex operators)
|
2026-02-13 21:52:03 +00:00
|
|
|
- **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:**
|
2026-02-16 22:55:04 +00:00
|
|
|
- `modifies` - Inherit from base schedule
|
2026-02-13 21:52:03 +00:00
|
|
|
- `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 }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule BakerSchedule modifies BaseWorkday {
|
2026-02-13 21:52:03 +00:00
|
|
|
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 }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule Baker modifies Base {
|
2026-02-13 21:52:03 +00:00
|
|
|
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 }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule Remote modifies Base {
|
2026-02-13 21:52:03 +00:00
|
|
|
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 {
|
2026-02-16 22:55:04 +00:00
|
|
|
on Fireday modifies BaseWorkday {
|
2026-02-13 21:52:03 +00:00
|
|
|
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
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule AnnualPattern modifies BaseWorkday {
|
2026-02-13 21:52:03 +00:00
|
|
|
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,
|
2026-02-16 22:55:04 +00:00
|
|
|
pub modifies: Option<Vec<String>>, // modifies BaseSchedule
|
2026-02-13 21:52:03 +00:00
|
|
|
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
|
2026-02-16 22:55:04 +00:00
|
|
|
pub modifies: Option<Vec<String>>, // Optional base schedule
|
2026-02-13 21:52:03 +00:00
|
|
|
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 = {
|
2026-02-16 22:55:04 +00:00
|
|
|
"schedule" <name:Ident> <modifies:ModifiesClause?> "{"
|
2026-02-13 21:52:03 +00:00
|
|
|
<items:ScheduleItem*>
|
2026-02-16 22:55:04 +00:00
|
|
|
"}" => Schedule { name, modifies, items, span }
|
2026-02-13 21:52:03 +00:00
|
|
|
};
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
ModifiesClause: Vec<String> = {
|
|
|
|
|
"modifies" <QualifiedPath>
|
2026-02-13 21:52:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 = {
|
2026-02-16 22:55:04 +00:00
|
|
|
<day:Ident> <modifies:ModifiesClause?> "{" <blocks:ScheduleBlock*> "}" =>
|
|
|
|
|
DayPattern { day, modifies, blocks, span }
|
2026-02-13 21:52:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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 }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule BakerSchedule modifies BaseWorkday {
|
2026-02-13 21:52:03 +00:00
|
|
|
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
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule BakerAnnual modifies BaseWorkday {
|
2026-02-13 21:52:03 +00:00
|
|
|
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 }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule Worker modifies BaseHuman {
|
2026-02-13 21:52:03 +00:00
|
|
|
block work { 9:00 - 17:00, action: GenericWork }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
schedule Baker modifies Worker {
|
2026-02-13 21:52:03 +00:00
|
|
|
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
|
2026-02-16 22:55:04 +00:00
|
|
|
- parent_schedule_id: Option<u32> // modifies reference
|
2026-02-13 21:52:03 +00:00
|
|
|
- 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
|
|
|
|
|
|
2026-02-16 22:55:04 +00:00
|
|
|
BakerSchedule modifies BaseWorkday
|
2026-02-13 21:52:03 +00:00
|
|
|
|
|
|
|
|
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)
|
2026-02-16 22:55:04 +00:00
|
|
|
1. Implement simplified `modifies`/`override` parsing
|
2026-02-13 21:52:03 +00:00
|
|
|
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
|
2026-02-16 22:55:04 +00:00
|
|
|
- [x] Simplified composition (`modifies`, `override` only)
|
2026-02-13 21:52:03 +00:00
|
|
|
- [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)
|