# 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 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` when None: ``` 0x00 ← 1 byte total ``` `Option` when Some(42): ``` 0x01 ← discriminant (Some) 0x2A 0x00 0x00 0x00 ← StringRef = 42 (u32) ← 5 bytes total ``` `Option>` when None: ``` 0x00 ← 1 byte total ``` `Option>` 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 ``` 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 - 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 When `T` itself is variable-length, each item carries its own size information: **Example: Vec** ``` 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** (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** 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 fields: Map template_refs: Vec // Templates this character uses behavior_links: Vec // NEW in v0.2.0 schedule_links: Vec // 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 // 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 // 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: ``` **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) // Qualified path 0x09 = List(Vec) 0x0A = Object(Vec) 0x0B = ProseBlock(StringRef tag, String content) 0x0C = Override(...) ``` ### 6.6 Expression Encoding ``` Expression: discriminant: u8 data: ``` **Discriminants:** ``` 0x01 = IntLit(i64) 0x02 = FloatLit(f64) 0x03 = StringLit(StringRef) 0x04 = BoolLit(bool) 0x05 = Identifier(Vec) 0x06 = FieldAccess(Box, StringRef) 0x07 = Comparison(Box, CompOp, Box) 0x08 = Logical(Box, LogicalOp, Box) 0x09 = Unary(UnaryOp, Box) 0x0A = Quantifier(QuantifierKind, StringRef var, Box collection, Box 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 fields: Map ``` --- ## 8. Section 6: Species ``` Count: u32 Species: [Species; Count] Species: name: StringRef includes: Vec fields: Map ``` --- ## 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: ``` #### 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 // NEW in v0.2.0 children: Vec ``` **Keyword Mapping:** `selector` or `choose` #### Sequence Node (0x02) ``` label: Option // NEW in v0.2.0 children: Vec ``` **Keyword Mapping:** `sequence` or `then` #### Condition Node (0x03) ``` expression: Expression ``` **Keyword Mapping:** `if` or `when` #### Action Node (0x04) ``` name: StringRef parameters: Vec ``` **Keyword Mapping:** No prefix (just action name) #### Decorator Nodes (0x10-0x19) **DecoratorRepeat (0x10):** ``` child: Box ``` Keyword: `repeat { ... }` **DecoratorRepeatN (0x11):** ``` count: u32 child: Box ``` Keyword: `repeat(N) { ... }` **DecoratorRepeatRange (0x12):** ``` min: u32 max: u32 child: Box ``` Keyword: `repeat(min..max) { ... }` **DecoratorInvert (0x13):** ``` child: Box ``` Keyword: `invert { ... }` **DecoratorRetry (0x14):** ``` max_attempts: u32 child: Box ``` Keyword: `retry(N) { ... }` **DecoratorTimeout (0x15):** ``` milliseconds: u64 // Duration in milliseconds child: Box ``` Keyword: `timeout(duration) { ... }` Example: `timeout(5s)`, `timeout(30m)`, `timeout(2h)` **DecoratorCooldown (0x16):** ``` milliseconds: u64 // Cooldown period in milliseconds child: Box ``` Keyword: `cooldown(duration) { ... }` **DecoratorIf (0x17):** ``` condition: Expression child: Box ``` Keyword: `if(condition) { ... }` **DecoratorSucceedAlways (0x18):** ``` child: Box ``` Keyword: `succeed_always { ... }` **DecoratorFailAlways (0x19):** ``` child: Box ``` Keyword: `fail_always { ... }` #### SubTree Node (0x20) ``` path: Vec // 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 // Index into SCHEDULES section (for inheritance) blocks: Vec patterns: Vec // 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> // Reference to behavior (qualified path) fields: Map ``` **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 // Pattern-specific data blocks: Vec // 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 // 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: Discriminants: 0x01 = Every(u32 days) // every N days 0x02 = WeeklyOn(Vec) // 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 behavior_links: Vec // NEW in v0.2.0 schedule_links: Vec // 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 fields: Map Participant: role: Option name: Vec // Qualified path self_block: Option> other_block: Option> ``` --- ## 13. Section 11: Locations ``` Count: u32 Locations: [Location; Count] Location: name: StringRef fields: Map ``` --- ## 14. Section 12: Life Arcs ``` Count: u32 LifeArcs: [LifeArc; Count] LifeArc: name: StringRef states: Vec ArcState: name: StringRef on_enter: Option> transitions: Vec 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 // Encoded as below ``` **Variants encoding (Vec):** ``` 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**