# Storybook Intermediate Representation (SBIR) v0.3.2 Specification

**Version:** 0.3.2
**Status:** Draft
**Date:** February 2026

---

## 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.3.2

**Keyword changes:**
1. **Definition Keyword** - The `concept_comparison` keyword has been renamed to `definition`
   - Old: `concept_comparison SkillLevel { ... }`
   - New: `definition SkillLevel { ... }`
   - This is a **find-and-replace** change - no binary format changes required
   - The struct and AST type is renamed from `ConceptComparisonDecl` to `DefinitionDecl`

### 1.4 Changes in v0.3.1

**Keyword changes:**
1. **Schedule Composition** - The `extends` keyword for schedule inheritance has been renamed to `modifies`
   - Old: `schedule BakerSchedule extends WorkWeek { ... }`
   - New: `schedule BakerSchedule modifies WorkWeek { ... }`
   - This is a **find-and-replace** change - no binary format changes required
   - The field name in the Schedule struct remains the same (just stores the base schedule name)

### 1.4 Changes in v0.3.0

**Major additions:**
1. **Type System** - Concepts, sub-concepts (enum/record), and concept comparisons with pattern matching
2. **Species-Based Template Inheritance** - Templates can declare a species base for field inheritance
3. **Life Arc Field Requirements** - Life arcs can declare required fields with type annotations

**Breaking changes:**
- TYPES section now populated with concept, sub_concept, and definition declarations
- Value discriminants renamed: Int→Number, Float→Decimal, String→Text, Bool→Boolean
- Expression discriminants renamed: IntLit→NumberLit, FloatLit→DecimalLit, StringLit→TextLit, BoolLit→BooleanLit
- TEMPLATES section extended with species_base field
- LIFE ARCS section extended with required_fields

### 1.5 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 (0x0003 for v0.3.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
- v0.3.0: Type system, species inheritance, life arc requirements

---

## 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:** New in v0.3.0. This section encodes the concept type system.

### 5.1 Structure

```
ConceptCount: u32
Concepts: [Concept; ConceptCount]
SubConceptCount: u32
SubConcepts: [SubConcept; SubConceptCount]
ConceptComparisonCount: u32
ConceptComparisons: [ConceptComparison; ConceptComparisonCount]
```

### 5.2 Concept Encoding

```
Concept:
  name: StringRef
```

A concept is a named type declaration with no additional data. Sub-concepts reference
their parent concept by name.

### 5.3 SubConcept Encoding

```
SubConcept:
  name: StringRef
  parent_concept: StringRef
  kind: u8
  data: <depends on kind>
```

**Kind Discriminants:**
```
0x01 = Enum
0x02 = Record
```

**Enum (0x01):**
```
variants: Vec<StringRef>
```

**Record (0x02):**
```
fields: Vec<Field>
```

Where `Field` is encoded as:
```
Field:
  name: StringRef
  value: Value
```

### 5.4 ConceptComparison Encoding

```
ConceptComparison:
  name: StringRef
  concept_ref: StringRef             // The concept being compared
  sub_concept_ref: StringRef         // The sub-concept (enum) being matched
  arms: Vec<ComparisonArm>
```

**ComparisonArm:**
```
ComparisonArm:
  variant: StringRef                 // Enum variant to match
  fields: Vec<Field>                 // Field assignments for this arm
  condition: Option<Expression>      // Optional guard condition
```

### 5.5 Binary Example

**Source:**
```storybook
concept Cup
sub_concept Cup.Type { Small, Medium, Large }
sub_concept Cup.Material { weight: 100, fragile: true }

definition CupDefaults for Cup matching Cup.Type {
    Small { capacity: 200 }
    Medium { capacity: 350 }
    Large { capacity: 500 }
}
```

**Binary (conceptual):**
```
ConceptCount: 1
Concepts:
  [0]: name = StringRef("Cup")

SubConceptCount: 2
SubConcepts:
  [0]: SubConcept {
    name: StringRef("Type")
    parent_concept: StringRef("Cup")
    kind: 0x01 (Enum)
    variants: [StringRef("Small"), StringRef("Medium"), StringRef("Large")]
  }
  [1]: SubConcept {
    name: StringRef("Material")
    parent_concept: StringRef("Cup")
    kind: 0x02 (Record)
    fields: [
      Field { name: StringRef("weight"), value: Number(100) },
      Field { name: StringRef("fragile"), value: Boolean(true) }
    ]
  }

ConceptComparisonCount: 1
ConceptComparisons:
  [0]: ConceptComparison {
    name: StringRef("CupDefaults")
    concept_ref: StringRef("Cup")
    sub_concept_ref: StringRef("Type")
    arms: [
      { variant: StringRef("Small"), fields: [("capacity", Number(200))], condition: None },
      { variant: StringRef("Medium"), fields: [("capacity", Number(350))], condition: None },
      { variant: StringRef("Large"), fields: [("capacity", Number(500))], condition: None }
    ]
  }
```

---

## 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 = Number(i64)                               // Renamed from Int in v0.3.0
0x02 = Decimal(f64)                              // Renamed from Float in v0.3.0
0x03 = Text(StringRef)                           // Renamed from String in v0.3.0
0x04 = Boolean(bool)                             // Renamed from Bool in v0.3.0
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(...)
```

**Note:** The wire format (discriminant bytes 0x01-0x0C) is unchanged from v0.2.0.
Only the semantic names have been updated to match the Storybook language's type
terminology.

### 6.6 Expression Encoding

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

**Discriminants:**
```
0x01 = NumberLit(i64)                            // Renamed from IntLit in v0.3.0
0x02 = DecimalLit(f64)                           // Renamed from FloatLit in v0.3.0
0x03 = TextLit(StringRef)                        // Renamed from StringLit in v0.3.0
0x04 = BooleanLit(bool)                          // Renamed from BoolLit in v0.3.0
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
  species_base: Option<StringRef>    // NEW in v0.3.0 - Species base for field inheritance
  strict: bool
  includes: Vec<StringRef>
  fields: Map<StringRef, Value>
```

**Species Base (NEW in v0.3.0):**

When `species_base` is `Some(ref)`, the template inherits fields from the referenced
species as its base layer. The override chain is:

1. Species fields (base layer)
2. Included template fields (override species)
3. Template's own fields (override includes)
4. Character fields (override template)

Last-one-wins semantics apply at each layer. Type invariance is enforced: a field's
type cannot change through the inheritance chain (e.g., a Number field in the species
cannot become a Text field in the template).

---

## 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
  required_fields: Vec<FieldRequirement>   // NEW in v0.3.0
  states: Vec<ArcState>

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

Transition:
  to: StringRef
  condition: Expression
```

### 14.1 FieldRequirement (NEW in v0.3.0)

```
FieldRequirement:
  name: StringRef                          // Field name
  type_name: StringRef                     // Expected type (e.g., "Number", "Text")
```

**Purpose:** Life arcs can declare required fields that any character using the life arc
must have. This enables compile-time validation that characters provide the necessary
fields for state transitions and on_enter actions.

**Example:**
```storybook
life_arc Career requires { skill_level: Number, title: Text } {
    state Junior { ... }
    state Senior { ... }
}
```

**Binary:**
```
LifeArc:
  name: StringRef("Career")
  required_fields: [
    FieldRequirement { name: StringRef("skill_level"), type_name: StringRef("Number") },
    FieldRequirement { name: StringRef("title"), type_name: StringRef("Text") }
  ]
  states: [...]
```

---

## 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

**Note:** As of v0.3.0, enum-like types can also be defined via `sub_concept` with
enum kind (Section 5.3). The key distinction is that `sub_concept` enums are tied to a
parent concept and participate in the concept type system (concept comparisons,
exhaustiveness checking), while standalone enums in this section are untyped
enumerations used primarily for calendar patterns and simple value sets.

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

---

## 16. Changelog

### v0.3.0 (February 2026)

**Major Features:**
- Type system: concepts, sub-concepts (enum and record kinds), concept comparisons
- Species-based template inheritance with type invariance enforcement
- Life arc field requirements with type annotations
- Value/expression type renames aligned with Storybook language terminology

**Breaking Changes:**
- TYPES section (Section 3) now populated with concept, sub_concept, and definition declarations
- Value discriminant names changed: Int→Number, Float→Decimal, String→Text, Bool→Boolean (wire format unchanged)
- Expression discriminant names changed: IntLit→NumberLit, FloatLit→DecimalLit, StringLit→TextLit, BoolLit→BooleanLit (wire format unchanged)
- TEMPLATES section: added `species_base: Option<StringRef>` field before `strict`
- LIFE ARCS section: added `required_fields: Vec<FieldRequirement>` field after `name`

**Note:** SBIR encoder/decoder implementation is deferred until SaberVM design is finalized.

### 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", Number(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 |
| 0.3.0 | Feb 2026 | Type system, species inheritance, life arc requirements |
| 0.3.1 | Feb 2026 | Schedule keyword change: `extends` → `modifies` (source-level only) |

---

**END OF SPECIFICATION**
