1258 lines
31 KiB
Markdown
1258 lines
31 KiB
Markdown
|
|
# 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 concept_comparison definitions
|
||
|
|
- 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 }
|
||
|
|
|
||
|
|
concept_comparison 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 concept_comparison definitions
|
||
|
|
- 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**
|