# Storybook Intermediate Representation (SBIR) v0.2.0 Specification

**Version:** 0.2.0
**Status:** Archived (superseded by v0.3.0)
**Date:** February 2026

> **Note:** This is a historical specification for SBIR v0.2.0. The current specification
> is [SBIR v0.3.0](./SBIR-v0.3.0-SPEC.md). Type names in this document (Int, Float,
> String, Bool) reflect v0.2.0 terminology; v0.3.0 renamed these to Number, Decimal,
> Text, and Boolean respectively.

---

## Table of Contents

1. [Introduction](#1-introduction)
2. [File Format Overview](#2-file-format-overview)
3. [Section 1: Header](#3-section-1-header)
4. [Section 2: String Table](#4-section-2-string-table)
5. [Section 3: Types](#5-section-3-types)
6. [Section 4: Characters](#6-section-4-characters)
7. [Section 5: Templates](#7-section-5-templates)
8. [Section 6: Species](#8-section-6-species)
9. [Section 7: Behaviors](#9-section-7-behaviors)
10. [Section 8: Schedules](#10-section-8-schedules)
11. [Section 9: Institutions](#11-section-9-institutions)
12. [Section 10: Relationships](#12-section-10-relationships)
13. [Section 11: Locations](#13-section-11-locations)
14. [Section 12: Life Arcs](#14-section-12-life-arcs)
15. [Section 13: Enums](#15-section-13-enums)
16. [Changelog](#16-changelog)

---

## 1. Introduction

### 1.1 Purpose

The Storybook Intermediate Representation (SBIR) is a binary format that represents compiled Storybook programs. It serves as the interchange format between the Storybook compiler and runtime engines.

### 1.2 Design Goals

- **Compact:** Efficient binary encoding for large story worlds
- **Fast to load:** Direct memory mapping when possible
- **Versioned:** Clear version tracking for format evolution
- **Complete:** Represents all semantic information from source
- **Runtime-ready:** Minimal post-processing required

### 1.3 Changes in v0.2.0

**Major additions:**
1. **Resource Linking System** - Characters and institutions can link to behaviors and schedules with conditions and priorities
2. **Year-Long Schedule System** - Schedules support temporal patterns (day-specific, seasonal, recurrence)
3. **Behavior Tree Enhancements** - Named nodes, decorator parameters, keyword transformations

**Breaking changes:**
- CHARACTERS section extended with behavior_links and schedule_links
- INSTITUTIONS section extended with behavior_links and schedule_links
- SCHEDULES section redesigned with patterns and inheritance
- BEHAVIORS section extended with named nodes and parameterized decorators

---

## 2. File Format Overview

### 2.1 File Structure

```
[Header]
[String Table]
[Type Definitions]
[Characters Section]
[Templates Section]
[Species Section]
[Behaviors Section]
[Schedules Section]
[Institutions Section]
[Relationships Section]
[Locations Section]
[Life Arcs Section]
[Enums Section]
```

### 2.2 Primitive Types

| Type | Size | Description |
|------|------|-------------|
| `u8` | 1 byte | Unsigned 8-bit integer |
| `u16` | 2 bytes | Unsigned 16-bit integer (little-endian) |
| `u32` | 4 bytes | Unsigned 32-bit integer (little-endian) |
| `u64` | 8 bytes | Unsigned 64-bit integer (little-endian) |
| `i32` | 4 bytes | Signed 32-bit integer (little-endian) |
| `i64` | 8 bytes | Signed 64-bit integer (little-endian) |
| `f32` | 4 bytes | IEEE 754 single-precision float |
| `f64` | 8 bytes | IEEE 754 double-precision float |
| `bool` | 1 byte | 0 = false, 1 = true |
| `String` | Variable | Length-prefixed UTF-8: `u32 length` + `[u8; length]` |
| `StringRef` | 4 bytes | Index into string table (u32) |

### 2.3 Common Structures

#### Option<T>

Optional values are encoded with a discriminant byte followed by the value if present:

```
u8 discriminant:
  0 = None  → No additional bytes, next field starts immediately
  1 = Some  → T data follows immediately after discriminant
```

**Encoding:**
- **None case:** Just 1 byte (0x00), nothing else
- **Some case:** 1 byte (0x01) + full T encoding

**Examples:**

`Option<StringRef>` when None:
```
0x00                           ← 1 byte total
```

`Option<StringRef>` when Some(42):
```
0x01                           ← discriminant (Some)
0x2A 0x00 0x00 0x00           ← StringRef = 42 (u32)
                               ← 5 bytes total
```

`Option<Vec<Field>>` when None:
```
0x00                           ← 1 byte total
```

`Option<Vec<Field>>` when Some([field1, field2]):
```
0x01                           ← discriminant (Some)
0x02 0x00 0x00 0x00           ← count = 2 (u32)
[Field 1 encoding]             ← First field
[Field 2 encoding]             ← Second field
                               ← 5+ bytes (depends on field sizes)
```

#### Vec<T>
```
u32 count
[T; count]
```

### 2.4 Binary Format Conventions

**IMPORTANT: SBIR is a packed binary format with no separators.**

#### No Delimiter Bytes

All data is laid out **sequentially in memory** with **no separator characters** between:
- Items in a Vec<T>
- Fields in a struct
- Sections in the file
- Values of any kind

The file is a continuous stream of bytes where each element's size determines where the next element begins.

#### Length-Prefix Pattern

All variable-length data uses a **length-prefix pattern**:

1. **Length/count field** (u32) tells you "how much data is coming"
2. **Data bytes** - read exactly that amount
3. **Next field** starts immediately after (no gap, no separator)

#### Reading Variable-Length Vec<T>

When `T` itself is variable-length, each item carries its own size information:

**Example: Vec<String>**
```
u32 count = 3                    ← "There are 3 strings"
  String 1:
    u32 length = 5               ← "First string is 5 bytes"
    [u8; 5] = "hello"            ← Read 5 bytes, next item starts immediately
  String 2:
    u32 length = 5               ← "Second string is 5 bytes"
    [u8; 5] = "world"            ← Read 5 bytes
  String 3:
    u32 length = 3               ← "Third string is 3 bytes"
    [u8; 3] = "foo"              ← Read 3 bytes
```

**Example: Vec<Character>** (complex variable-length structs)
```
u32 count = 2                    ← "There are 2 characters"
  Character 1:
    u32 name_len = 6
    [u8; 6] = "Martha"
    u8 species_discriminant = 1  ← Option::Some
    u32 species_ref = 10
    u32 fields_count = 2
      Field 1: (name_ref, value_discriminant, value_data)
      Field 2: (name_ref, value_discriminant, value_data)
    u32 template_refs_count = 1
      StringRef = 15
    u32 behavior_links_count = 0
    u32 schedule_links_count = 0
  Character 2:
    (starts immediately after Character 1 ends)
    u32 name_len = 5
    [u8; 5] = "David"
    ... (continues)
```

#### Parsing Algorithm

The parser reads **sequentially** using each length field to know how many bytes to consume:

```
position = 0
while position < file_size:
    read_length_or_discriminant()
    read_exactly_that_many_bytes()
    position += bytes_read
    // Next field starts here (no seeking, no separator scanning)
```

#### Key Rules

1. **Fixed-size types** (u8, u32, f64, etc.) take their exact size - no padding
2. **Variable-size types** always start with a length prefix (u32)
3. **Option<T>** starts with a discriminant byte (0=None, 1=Some)
4. **Enums/discriminated unions** start with a discriminant byte
5. **No alignment padding** - all data is tightly packed

There are no:

- No newline characters (`\n`)
- No separators (`,`, `;`, spaces)
- No null terminators (except inside UTF-8 string data)
- No padding bytes between fields (unless explicitly specified)
- No section markers or headers (sections just follow each other)

The documentation uses newlines and indentation for **readability only** - the actual binary file is a continuous stream of bytes with no whitespace.

---

## 3. Section 1: Header

```
Magic: [u8; 4]     // "SBIR" (0x53 0x42 0x49 0x52)
Version: u16       // Major version (0x0002 for v0.2.0)
MinorVersion: u16  // Minor version (0x0000)
Flags: u32         // Reserved (0x00000000)
SectionCount: u32  // Number of sections (currently 13)
```

**Version History:**
- v0.1.0: Implicit version (pre-release)
- v0.2.0: First formal versioned release

---

## 4. Section 2: String Table

The string table stores all strings used in the SBIR file.

```
Count: u32
Strings: [String; Count]
```

**Usage:** All `StringRef` types reference an index in this table.

**Encoding:** UTF-8 with length prefix.

**Example:**
```
Count: 3
Strings:
  [0]: "Alice"
  [1]: "Wonderland"
  [2]: "rabbit_hole"
```

---

## 5. Section 3: Types

**Note:** This section is reserved for future type system enhancements. Currently unused.

```
Count: u32  // 0 in v0.2.0
Types: []
```

---

## 6. Section 4: Characters

### 6.1 Structure

```
Count: u32
Characters: [Character; Count]
```

### 6.2 Character Encoding

```
Character:
  name: StringRef
  species: Option<StringRef>
  fields: Map<StringRef, Value>
  template_refs: Vec<StringRef>          // Templates this character uses
  behavior_links: Vec<BehaviorLink>      // NEW in v0.2.0
  schedule_links: Vec<ScheduleLink>      // NEW in v0.2.0
```

### 6.3 BehaviorLink (NEW in v0.2.0)

```
BehaviorLink:
  behavior_id: u32                       // Index into BEHAVIORS section
  priority: u8                           // 0=Low, 1=Normal, 2=High, 3=Critical
  condition: Option<Expression>          // Optional activation condition
  is_default: bool                       // Default behavior (no condition)
```

**Priority Encoding:**
```
enum Priority {
    Low = 0,
    Normal = 1,
    High = 2,
    Critical = 3,
}
```

**Selection Algorithm:**
1. Filter links where `condition` evaluates to true (or is None)
2. Sort by priority (descending)
3. Return highest priority link
4. If tie, use declaration order

### 6.4 ScheduleLink (NEW in v0.2.0)

```
ScheduleLink:
  schedule_id: u32                       // Index into SCHEDULES section
  condition: Option<Expression>          // Optional activation condition
  is_default: bool                       // Default schedule (fallback)
```

**Selection Algorithm:**
1. Iterate schedule_links in order
2. Skip default links initially
3. Return first link where `condition` is true (or None)
4. If no match, use default link (if present)

### 6.5 Value Encoding

```
Value:
  discriminant: u8
  data: <depends on discriminant>
```

**Discriminants:**
```
0x01 = Int(i64)
0x02 = Float(f64)
0x03 = String(StringRef)
0x04 = Bool(bool)
0x05 = Range(Value, Value)
0x06 = Time(u8 hour, u8 minute, u8 second)
0x07 = Duration(u32 hours, u32 minutes, u32 seconds)
0x08 = Identifier(Vec<StringRef>)  // Qualified path
0x09 = List(Vec<Value>)
0x0A = Object(Vec<Field>)
0x0B = ProseBlock(StringRef tag, String content)
0x0C = Override(...)
```

### 6.6 Expression Encoding

```
Expression:
  discriminant: u8
  data: <depends on discriminant>
```

**Discriminants:**
```
0x01 = IntLit(i64)
0x02 = FloatLit(f64)
0x03 = StringLit(StringRef)
0x04 = BoolLit(bool)
0x05 = Identifier(Vec<StringRef>)
0x06 = FieldAccess(Box<Expr>, StringRef)
0x07 = Comparison(Box<Expr>, CompOp, Box<Expr>)
0x08 = Logical(Box<Expr>, LogicalOp, Box<Expr>)
0x09 = Unary(UnaryOp, Box<Expr>)
0x0A = Quantifier(QuantifierKind, StringRef var, Box<Expr> collection, Box<Expr> predicate)
```

**CompOp:** `u8` (0x01=Eq, 0x02=Ne, 0x03=Lt, 0x04=Le, 0x05=Gt, 0x06=Ge)

**LogicalOp:** `u8` (0x01=And, 0x02=Or)

**UnaryOp:** `u8` (0x01=Not, 0x02=Neg)

**QuantifierKind:** `u8` (0x01=ForAll, 0x02=Exists)

---

## 7. Section 5: Templates

```
Count: u32
Templates: [Template; Count]

Template:
  name: StringRef
  strict: bool
  includes: Vec<StringRef>
  fields: Map<StringRef, Value>
```

---

## 8. Section 6: Species

```
Count: u32
Species: [Species; Count]

Species:
  name: StringRef
  includes: Vec<StringRef>
  fields: Map<StringRef, Value>
```

---

## 9. Section 7: Behaviors

### 9.1 Structure

```
Count: u32
Behaviors: [Behavior; Count]
```

### 9.2 Behavior Encoding

```
Behavior:
  name: StringRef
  root: BehaviorNode
```

### 9.3 BehaviorNode Encoding

```
BehaviorNode:
  discriminant: u8
  data: <depends on discriminant>
```

#### Node Type Discriminants

```
0x01 = Selector
0x02 = Sequence
0x03 = Condition
0x04 = Action
0x10 = DecoratorRepeat
0x11 = DecoratorRepeatN
0x12 = DecoratorRepeatRange
0x13 = DecoratorInvert
0x14 = DecoratorRetry
0x15 = DecoratorTimeout
0x16 = DecoratorCooldown
0x17 = DecoratorGuard
0x18 = DecoratorSucceedAlways
0x19 = DecoratorFailAlways
0x20 = SubTree
```

#### Selector Node (0x01)

```
label: Option<StringRef>               // NEW in v0.2.0
children: Vec<BehaviorNode>
```

**Keyword Mapping:** `selector` or `choose`

#### Sequence Node (0x02)

```
label: Option<StringRef>               // NEW in v0.2.0
children: Vec<BehaviorNode>
```

**Keyword Mapping:** `sequence` or `then`

#### Condition Node (0x03)

```
expression: Expression
```

**Keyword Mapping:** `if` or `when`

#### Action Node (0x04)

```
name: StringRef
parameters: Vec<Field>
```

**Keyword Mapping:** No prefix (just action name)

#### Decorator Nodes (0x10-0x19)

**DecoratorRepeat (0x10):**
```
child: Box<BehaviorNode>
```
Keyword: `repeat { ... }`

**DecoratorRepeatN (0x11):**
```
count: u32
child: Box<BehaviorNode>
```
Keyword: `repeat(N) { ... }`

**DecoratorRepeatRange (0x12):**
```
min: u32
max: u32
child: Box<BehaviorNode>
```
Keyword: `repeat(min..max) { ... }`

**DecoratorInvert (0x13):**
```
child: Box<BehaviorNode>
```
Keyword: `invert { ... }`

**DecoratorRetry (0x14):**
```
max_attempts: u32
child: Box<BehaviorNode>
```
Keyword: `retry(N) { ... }`

**DecoratorTimeout (0x15):**
```
milliseconds: u64                      // Duration in milliseconds
child: Box<BehaviorNode>
```
Keyword: `timeout(duration) { ... }`
Example: `timeout(5s)`, `timeout(30m)`, `timeout(2h)`

**DecoratorCooldown (0x16):**
```
milliseconds: u64                      // Cooldown period in milliseconds
child: Box<BehaviorNode>
```
Keyword: `cooldown(duration) { ... }`

**DecoratorIf (0x17):**
```
condition: Expression
child: Box<BehaviorNode>
```
Keyword: `if(condition) { ... }`

**DecoratorSucceedAlways (0x18):**
```
child: Box<BehaviorNode>
```
Keyword: `succeed_always { ... }`

**DecoratorFailAlways (0x19):**
```
child: Box<BehaviorNode>
```
Keyword: `fail_always { ... }`

#### SubTree Node (0x20)

```
path: Vec<StringRef>                   // Qualified path to subtree
```

Keyword: `include path::to::subtree`

---

## 10. Section 8: Schedules

### 10.1 Structure

```
Count: u32
Schedules: [Schedule; Count]
```

### 10.2 Schedule Encoding (REDESIGNED in v0.2.0)

```
Schedule:
  name: StringRef
  parent_schedule_id: Option<u32>      // Index into SCHEDULES section (for inheritance)
  blocks: Vec<ScheduleBlock>
  patterns: Vec<SchedulePattern>       // Day-specific, seasonal, recurrence patterns
```

### 10.3 ScheduleBlock

```
ScheduleBlock:
  name: StringRef                      // Required in v0.2.0
  start: u16                           // Minutes since midnight (0-1439)
  end: u16                             // Minutes since midnight (0-1439)
  behavior_ref: Option<Vec<StringRef>> // Reference to behavior (qualified path)
  fields: Map<StringRef, Value>
```

**Changes from v0.1.0:**
- `name` is now required (was optional)
- `behavior_ref` replaces `activity: String`
- Time is encoded as minutes since midnight

### 10.4 SchedulePattern (NEW in v0.2.0)

```
SchedulePattern:
  kind: u8                             // Pattern discriminant
  specification: Vec<u8>               // Pattern-specific data
  blocks: Vec<ScheduleBlock>           // Blocks to apply when pattern matches
```

**Pattern Kind Discriminants:**
```
0x01 = DayPattern
0x02 = SeasonPattern
0x03 = RecurrencePattern
```

#### DayPattern (0x01)

```
specification:
  day_enum_value: StringRef            // References user-defined DayOfWeek enum
```

Example: `on Fireday` → references "Fireday" from user's DayOfWeek enum

#### SeasonPattern (0x02)

```
specification:
  season_enum_values: Vec<StringRef>   // Multiple seasons allowed
```

Example: `season (EarlySummer, LateSummer)` → references Season enum values

#### RecurrencePattern (0x03)

```
specification:
  name: StringRef                      // Recurrence name
  spec: RecurrenceSpec
```

**RecurrenceSpec:**
```
RecurrenceSpec:
  discriminant: u8
  data: <depends on discriminant>

Discriminants:
  0x01 = Every(u32 days)               // every N days
  0x02 = WeeklyOn(Vec<StringRef>)      // weekly on [days]
  0x03 = MonthlyOnDay(u8 day)          // monthly on day N
  0x04 = Annually(u8 month, u8 day)    // annually on month/day
```

### 10.5 Runtime Schedule Evaluation

**Algorithm:**
1. Resolve character's schedule via ScheduleLink (conditional selection)
2. Merge inherited schedules (parent → child, depth-first)
3. Evaluate patterns for current day/season
4. Overlay patterns on base blocks (later patterns override earlier ones)
5. Produce final 24-hour schedule
6. Return ordered list of (time, behavior_ref) pairs

---

## 11. Section 9: Institutions

### 11.1 Structure (EXTENDED in v0.2.0)

```
Count: u32
Institutions: [Institution; Count]

Institution:
  name: StringRef
  fields: Map<StringRef, Value>
  behavior_links: Vec<BehaviorLink>    // NEW in v0.2.0
  schedule_links: Vec<ScheduleLink>    // NEW in v0.2.0
```

**Note:** BehaviorLink and ScheduleLink are identical to Character section (§6.3, §6.4)

---

## 12. Section 10: Relationships

```
Count: u32
Relationships: [Relationship; Count]

Relationship:
  name: StringRef
  participants: Vec<Participant>
  fields: Map<StringRef, Value>

Participant:
  role: Option<StringRef>
  name: Vec<StringRef>                 // Qualified path
  self_block: Option<Vec<Field>>
  other_block: Option<Vec<Field>>
```

---

## 13. Section 11: Locations

```
Count: u32
Locations: [Location; Count]

Location:
  name: StringRef
  fields: Map<StringRef, Value>
```

---

## 14. Section 12: Life Arcs

```
Count: u32
LifeArcs: [LifeArc; Count]

LifeArc:
  name: StringRef
  states: Vec<ArcState>

ArcState:
  name: StringRef
  on_enter: Option<Vec<Field>>
  transitions: Vec<Transition>

Transition:
  to: StringRef
  condition: Expression
```

---

## 15. Section 13: Enums

### 15.1 Structure

```
Count: u32
Enums: [EnumDecl; Count]
```

### 15.2 EnumDecl Encoding

```
EnumDecl:
  name: StringRef                      // u32 index into string table
  variants: Vec<StringRef>             // Encoded as below
```

**Variants encoding (Vec<StringRef>):**
```
u32 variant_count                      // Number of enum variants
[StringRef; variant_count]             // Array of variant names (each is u32)
```

### 15.3 Binary Example

**Source:**
```storybook
enum SkillLevel {
    Novice,
    Beginner,
    Intermediate,
    Advanced,
    Expert,
    Master
}
```

**Binary encoding:**
```
EnumDecl:
  name: StringRef = 42                 // "SkillLevel" (4 bytes)

  u32 variant_count = 6                // 6 variants (4 bytes)
  variants:
    StringRef = 43                     // "Novice" (4 bytes)
    StringRef = 44                     // "Beginner" (4 bytes)
    StringRef = 45                     // "Intermediate" (4 bytes)
    StringRef = 46                     // "Advanced" (4 bytes)
    StringRef = 47                     // "Expert" (4 bytes)
    StringRef = 48                     // "Master" (4 bytes)

Total: 32 bytes (4 + 4 + 6*4)
```

### 15.4 Usage

**Usage:** Enums are used for:
- Calendar definitions (DayOfWeek, Season, Month)
- Custom enumerated values
- Pattern matching in schedules

**Standard Calendar Enums (optional):**
- `DayOfWeek`: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
- `Season`: Spring, Summer, Fall, Winter
- `Month`: January, February, ..., December

---

## 16. Changelog

### v0.2.0 (February 2026)

**Major Features:**
- Resource linking system for behaviors and schedules
- Year-long schedule patterns (day, season, recurrence)
- Schedule inheritance and composition
- Behavior tree keyword support (named nodes)
- Parameterized decorators (repeat, retry, timeout, cooldown, if)

**Breaking Changes:**
- CHARACTERS section: added behavior_links, schedule_links
- INSTITUTIONS section: added behavior_links, schedule_links
- SCHEDULES section: complete redesign with patterns and inheritance
- BEHAVIORS section: added named node support

**Deprecations:**
- None (first versioned release)

**Bug Fixes:**
- N/A (first formal release)

### v0.1.0 (Implicit, Pre-Release)

**Initial format** (inferred from existing codebase):
- Basic entity storage (characters, templates, species)
- Simple schedules (time blocks with activities)
- Behavior trees (symbolic syntax)
- Relationships, locations, life arcs
- Enum support

---

## Appendix A: Binary Encoding Examples

### Example 1: Character with Resource Links

**Source:**
```storybook
character Alice: Human {
    age: 7

    uses behavior: CuriousBehavior, when: self.location == Wonderland, priority: High
    uses behavior: DefaultBehavior, default: true

    uses schedule: AdventureSchedule, when: self.in_wonderland
    uses schedule: NormalSchedule, default: true
}
```

**Binary (conceptual):**
```
Character:
  name: StringRef(0)         // "Alice"
  species: Some(StringRef(1)) // "Human"
  fields: [
    ("age", Int(7))
  ]
  template_refs: []
  behavior_links: [
    BehaviorLink {
      behavior_id: 3
      priority: 2              // High
      condition: Some(Comparison(FieldAccess(...), Eq, Identifier(...)))
      is_default: false
    },
    BehaviorLink {
      behavior_id: 5
      priority: 1              // Normal
      condition: None
      is_default: true
    }
  ]
  schedule_links: [
    ScheduleLink {
      schedule_id: 1
      condition: Some(FieldAccess(...))
      is_default: false
    },
    ScheduleLink {
      schedule_id: 2
      condition: None
      is_default: true
    }
  ]
```

### Example 2: Schedule with Patterns

**Source:**
```storybook
schedule WorkWeek modifies BaseSchedule {
    block morning { 08:00 - 12:00: WorkTasks }
    block lunch { 12:00 - 13:00: EatLunch }
    block afternoon { 13:00 - 17:00: WorkTasks }

    on Friday {
        override afternoon { 13:00 - 15:00: FinishWeek }
    }

    season (Summer) {
        override morning { 07:00 - 11:00: WorkEarly }
    }
}
```

**Binary (conceptual):**
```
Schedule:
  name: StringRef(10)        // "WorkWeek"
  parent_schedule_id: Some(0) // BaseSchedule index
  blocks: [
    ScheduleBlock {
      name: StringRef(11)    // "morning"
      start: 480             // 08:00 in minutes
      end: 720               // 12:00 in minutes
      behavior_ref: Some(["WorkTasks"])
      fields: []
    },
    ScheduleBlock {
      name: StringRef(12)    // "lunch"
      start: 720
      end: 780
      behavior_ref: Some(["EatLunch"])
      fields: []
    },
    ScheduleBlock {
      name: StringRef(13)    // "afternoon"
      start: 780
      end: 1020
      behavior_ref: Some(["WorkTasks"])
      fields: []
    }
  ]
  patterns: [
    SchedulePattern {
      kind: 0x01             // DayPattern
      specification: StringRef(14)  // "Friday"
      blocks: [
        ScheduleBlock {
          name: StringRef(13) // "afternoon" (override)
          start: 780
          end: 900
          behavior_ref: Some(["FinishWeek"])
          fields: []
        }
      ]
    },
    SchedulePattern {
      kind: 0x02             // SeasonPattern
      specification: [StringRef(15)]  // ["Summer"]
      blocks: [
        ScheduleBlock {
          name: StringRef(11) // "morning" (override)
          start: 420
          end: 660
          behavior_ref: Some(["WorkEarly"])
          fields: []
        }
      ]
    }
  ]
```

---

## Appendix B: File Size Estimates

**Assumptions:**
- Average character: 500 bytes
- Average behavior tree: 1 KB
- Average schedule: 800 bytes
- 1000 characters, 500 behaviors, 300 schedules

**Estimated size:**
- Characters: 500 KB
- Behaviors: 500 KB
- Schedules: 240 KB
- Other sections: 100 KB
- **Total: ~1.34 MB**

**Compression:** Typical compression (gzip/zstd) achieves 60-70% reduction → ~400-500 KB

---

## Appendix C: Version History

| Version | Date | Major Changes |
|---------|------|---------------|
| 0.1.0 | (Implicit) | Initial format |
| 0.2.0 | Feb 2026 | Resource linking, year-long schedules, behavior keywords |

---

**END OF SPECIFICATION**
