# Storybook Intermediate Representation (SBIR) v0.3.1 Specification **Version:** 0.3.1 **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.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 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 (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: ``` **Kind Discriminants:** ``` 0x01 = Enum 0x02 = Record ``` **Enum (0x01):** ``` variants: Vec ``` **Record (0x02):** ``` fields: Vec ``` 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: variant: StringRef // Enum variant to match fields: Vec // Field assignments for this arm condition: Option // 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 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 = 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) // Qualified path 0x09 = List(Vec) 0x0A = Object(Vec) 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: ``` **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) 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 species_base: Option // NEW in v0.3.0 - Species base for field inheritance strict: bool includes: Vec fields: Map ``` **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 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 required_fields: Vec // NEW in v0.3.0 states: Vec ArcState: name: StringRef on_enter: Option> transitions: Vec 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 // 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 **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` field before `strict` - LIFE ARCS section: added `required_fields: Vec` 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**