Files
storybook/design/behavior-tree-keywords-design.md

1358 lines
33 KiB
Markdown
Raw Permalink Normal View History

# Behavior Tree Keyword System Design
**Status:** Design Proposal
**Author:** Behavior Tree Language Designer (Agent 2)
**Date:** February 2026
**Related Tasks:** Task #3, Task #4
---
## Executive Summary
This document proposes replacing Storybook's symbolic behavior tree syntax (`?`, `>`, `*`, `~`, `!`, `@`) with human-friendly keywords that make the language accessible to storytellers without programming backgrounds. The new syntax maintains all existing functionality while dramatically improving readability and intuitiveness.
**Key Changes:**
- `?``selector` or `choose`
- `>``sequence` or `then`
- `*``repeat` (with optional parameters)
- `~` → Decorator keywords: `invert`, `retry(N)`, `timeout(duration)`, `cooldown(duration)`, `guard(condition)`
- `!``if` or `when` (condition nodes)
- `@` → No prefix needed for actions (just the action name)
- Subtrees: `@path::to::subtree``include path::to::subtree`
---
## 1. Current State Analysis
### 1.1 Existing Symbolic Constructs
| Sigil | Node Type | Current Semantics | AST Representation |
|-------|-----------|-------------------|-------------------|
| `?` | Selector | Try children in order; succeed on first success | `Selector(Vec<BehaviorNode>)` |
| `>` | Sequence | Run children in order; fail on first failure | `Sequence(Vec<BehaviorNode>)` |
| `!` | Condition | Evaluate expression; succeed if true | `Condition(Expr)` |
| `@` | Action | Execute named engine action | `Action(String, Vec<Field>)` |
| `~` | Decorator | Modify child behavior | `Decorator(String, Box<BehaviorNode>)` |
| `*` | Repeat | Infinite loop decorator | `repeat_node` (grammar only) |
| `@path` | SubTree | Reference to subtree | `SubTree(Vec<String>)` |
### 1.2 Examples from Codebase
**Current Symbolic Syntax:**
```sb
behavior WhiteRabbit_ConstantlyLate {
? {
> {
CheckPocketWatch
RealizeHowLate
MutterAnxiously
ScurryToDestination
}
> {
EncounterObstacle
DropGloves
DropFan
PanicFurther
}
}
}
```
**Current Limitations Identified:**
1. **Decorator syntax missing**: Multiple TODOs in examples mention "Add repeater decorator support"
2. **Sigils are cryptic**: `?` and `>` are not self-documenting
3. **No parameterized decorators**: Cannot express `repeat(3)`, `timeout(5s)`, `retry(2)`
4. **Condition syntax unclear**: `!` looks like negation rather than "if this condition"
5. **Action prefix redundant**: `@` adds noise when actions are self-evident from context
### 1.3 Design Spec vs Implementation Gap
**Design Spec (Section 6.1) promises:**
- Decorator support with `~` sigil
- Modifiers: invert, repeat, cooldown
- Semicolons OR newlines as separators
**Current Implementation:**
- Tree-sitter grammar has `repeat_node` with `*` sigil
- AST has `Decorator(String, Box<BehaviorNode>)`
- No parameterized decorator syntax in grammar
- Decorators mentioned in LSP code but not implemented in parser
**Conclusion:** Decorator functionality is partially designed but not fully implemented. This redesign is the perfect opportunity to implement it correctly with keyword syntax.
---
## 2. Proposed Keyword Mappings
### 2.1 Control Flow Keywords
#### Selector Node
**Current:** `? { ... }`
**Proposed:** `selector { ... }` OR `choose { ... }`
**Rationale:** "Choose" is more storytelling-friendly. "Selector" is technically accurate for developers.
**Recommendation:** Support both, with `choose` as the preferred form in examples. (i want `choose`, no `selector`)
**Example:**
```sb
// Before
? {
try_option_a
try_option_b
fallback
}
// After (storyteller-friendly)
choose {
try_option_a
try_option_b
fallback
}
// After (developer-friendly)
selector {
try_option_a
try_option_b
fallback
}
```
#### Sequence Node
**Current:** `> { ... }`
**Proposed:** `sequence { ... }` OR `then { ... }`
**Rationale:** "Then" reads naturally in storytelling contexts. "Sequence" is technically precise.
**Recommendation:** Support both, with context determining preference. (i want `then`, no `sequence`)
**Example:**
```sb
// Before
> {
check_energy
move_to_location
perform_action
}
// After (storyteller-friendly)
then {
check_energy
move_to_location
perform_action
}
// After (developer-friendly)
sequence {
check_energy
move_to_location
perform_action
}
```
### 2.2 Condition Keywords
**Current:** `! expression`
**Proposed:** `if(expression)` OR `when(expression)`
**Rationale:** Clear, universally understood conditional syntax.
**Recommendation:** Support both for flexibility. `when` is more narrative-friendly. (use `when`)
**Example:**
```sb
// Before
! need.any is urgent
// After (either form)
if(need.any is urgent)
when(need.any is urgent)
```
### 2.3 Action Keywords
**Current:** `@ action_name` or `@ action_name(params)`
**Proposed:** Remove prefix, just use action name
**Rationale:** Actions are distinguishable from control flow by context. No prefix needed. (yeah i'm fine with this)
**Example:**
```sb
// Before
@ move_to(counter)
@ serve_next_customer
// After
move_to(counter)
serve_next_customer
```
### 2.4 Subtree Keywords
**Current:** `@path::to::subtree`
**Proposed:** `include path::to::subtree`
**Rationale:** "Include" clearly indicates pulling in external definition. Distinct from actions. (perfect)
**Example:**
```sb
// Before
behavior WorkAtClinic {
? {
@HandleUrgentNeed
> treat_patients { ... }
}
}
// After
behavior WorkAtClinic {
choose {
include HandleUrgentNeed
then treat_patients { ... }
}
}
```
### 2.5 Decorator Keywords (idk, that would make this turing complete sooooo i mean i guess? this feels like too much but okay.)
**Current:** `~decorator_name { child }` (design spec, not implemented)
**Proposed:** Direct keyword syntax with optional parameters
#### Basic Decorators
**`repeat`** - Infinite loop
```sb
// Equivalent to current * sigil
repeat {
patrol
}
```
**`repeat(N)`** - Loop N times
```sb
repeat(3) {
knock_on_door
}
```
**`repeat(min..max)`** - Loop between min and max times
```sb
repeat(2..5) {
search_area
}
```
**`invert`** - Negate child result
```sb
invert {
if(enemy_nearby)
}
```
**`retry(N)`** - Retry up to N times on failure
```sb
retry(3) {
attempt_connection
}
```
**`timeout(duration)`** - Fail if child exceeds duration
```sb
timeout(5s) {
wait_for_response
}
```
**`cooldown(duration)`** - Prevent re-execution within duration
```sb
cooldown(30s) {
shout_warning
}
```
**`guard(condition)`** - Only run child if condition met
```sb
guard(energy > 50) {
sprint_to_safety
}
```
**`succeed_always`** - Always return success
```sb
succeed_always {
attempt_optional_task
}
```
**`fail_always`** - Always return failure (useful for debugging)
```sb
fail_always {
disabled_behavior
}
```
---
## 3. Complete Grammar Specification
### 3.1 Behavior Block Syntax
```ebnf
behavior_declaration ::= "behavior" identifier prose_blocks? "{" behavior_node "}"
behavior_node ::=
| selector_node
| sequence_node
| condition_node
| action_node
| decorator_node
| subtree_node
selector_node ::= ("selector" | "choose") "{" behavior_node+ "}"
sequence_node ::= ("sequence" | "then") "{" behavior_node+ "}"
condition_node ::= ("if" | "when") "(" expression ")"
action_node ::= identifier ( "(" action_params ")" )?
action_params ::= action_param ("," action_param)*
action_param ::= (identifier ":")? value
decorator_node ::= decorator_keyword decorator_params? "{" behavior_node "}"
decorator_keyword ::=
| "repeat"
| "invert"
| "retry"
| "timeout"
| "cooldown"
| "guard"
| "succeed_always"
| "fail_always"
decorator_params ::=
| "(" integer ")" // repeat(N), retry(N)
| "(" integer ".." integer ")" // repeat(min..max)
| "(" duration ")" // timeout(5s), cooldown(30s)
| "(" expression ")" // guard(condition)
subtree_node ::= "include" path_segments
duration ::= integer ("d" | "h" | "m" | "s")
```
### 3.2 Nested Syntax Support
**Decorators can nest:**
```sb
timeout(10s) {
retry(3) {
attempt_difficult_task
}
}
```
**Inline conditions within control flow:**
```sb
choose {
then {
if(threat_detected)
flee_to_safety
}
then {
if(resource_found)
gather_resource
}
idle_wait
}
```
**Named nodes (optional for complex trees):** (oooo i fucking love this, maybe this is the required design.)
```sb
choose root {
sequence handle_threat {
if(threat_detected)
flee_to_safety
}
sequence gather {
if(resource_found)
gather_resource
}
idle_wait
}
```
---
## 4. Example Transformations
### 4.1 Simple Behavior (White Rabbit)
**Before:**
```sb
behavior WhiteRabbit_ConstantlyLate {
? {
> {
CheckPocketWatch
RealizeHowLate
MutterAnxiously
ScurryToDestination
}
> {
EncounterObstacle
DropGloves
DropFan
PanicFurther
ReverseDirection
}
> {
SpotQueen
FlattenEarsInFear
TremblingBow
AwaitCommands
}
> {
CheckWatch
RunInCircles
CheckWatchAgain
}
}
}
```
**After:**
```sb
behavior WhiteRabbit_ConstantlyLate {
---description
Models the White Rabbit's perpetual anxiety about time and
his duties to the Queen. Uses a selector to try multiple
panic responses.
---
choose {
then {
CheckPocketWatch
RealizeHowLate
MutterAnxiously
ScurryToDestination
}
then {
EncounterObstacle
DropGloves
DropFan
PanicFurther
ReverseDirection
}
then {
SpotQueen
FlattenEarsInFear
TremblingBow
AwaitCommands
}
then {
CheckWatch
RunInCircles
CheckWatchAgain
}
}
}
```
### 4.2 Complex Behavior with Decorators (New)
**Current (with TODO):**
```sb
behavior MadTeaParty_CoordinatedMadness {
---description
Models the Mad Hatter's perpetual tea party that loops forever.
---
// TODO: Add repeater decorator support
> {
// Selector: Choose a mad activity
? {
> {
PoseRiddle
WaitForAnswer
DeclareAnswerWrong
}
> {
SwitchSeats
CleanDirtyTeacup
PourFreshTea
}
> {
WakeDormouse
ListenToBriefStory
StuffDormouseInTeapot
}
}
}
}
```
**After (with decorators implemented):**
```sb
behavior MadTeaParty_CoordinatedMadness {
---description
Models the Mad Hatter's perpetual tea party that loops forever at 6 o'clock.
The party never ends, cycling through mad activities indefinitely.
---
repeat {
choose {
then {
PoseRiddle
WaitForAnswer
DeclareAnswerWrong
}
then {
SwitchSeats
CleanDirtyTeacup
PourFreshTea
}
then {
WakeDormouse
ListenToBriefStory
StuffDormouseInTeapot
}
}
}
}
```
### 4.3 Advanced Behavior with Guards and Timeouts
**New capability:**
```sb
behavior CheshireCat_Materialization {
---description
Complex behavior modeling the Cheshire Cat's gradual materialization
and dematerialization. Uses decorators to add timing and conditions.
---
choose {
then {
if(alice_nearby and visibility < 0.1)
timeout(10s) {
repeat(5) {
IncreaseVisibility(0.2)
PauseForEffect(1s)
}
}
MaterializeGrin
SpeakInRiddles
}
then {
if(visibility > 0.9)
cooldown(30s) {
timeout(8s) {
repeat(5) {
DecreaseVisibility(0.2)
PauseForEffect(1s)
}
}
}
VanishExceptGrin
}
guard(visibility >= 0.5) {
IdleFloat
}
}
}
```
### 4.4 Behavior with Retries
**New capability:**
```sb
behavior ExecutionerDuty {
---description
The executioner attempts to behead people, but always fails
because the Queen's targets often vanish or are cards.
---
choose {
then {
if(execution_ordered)
retry(3) {
sequence {
LocateTarget
PrepareAxe
AttemptBeheading
}
}
ReportFailureToQueen
ReceiveBeheadingThreat
}
repeat {
WaitForOrders
}
}
}
```
---
## 5. SBIR Representation
The SBIR (Storybook Intermediate Representation) must encode all behavior tree nodes with their full semantics.
### 5.1 Node Type Encoding
```rust
// SBIR Section 7: BEHAVIOR_TREES
enum BehaviorNodeType {
Selector = 0x01,
Sequence = 0x02,
Condition = 0x03,
Action = 0x04,
// Decorators: 0x10-0x1F range
DecoratorRepeat = 0x10,
DecoratorRepeatN = 0x11,
DecoratorRepeatRange = 0x12,
DecoratorInvert = 0x13,
DecoratorRetry = 0x14,
DecoratorTimeout = 0x15,
DecoratorCooldown = 0x16,
DecoratorGuard = 0x17,
DecoratorSucceedAlways = 0x18,
DecoratorFailAlways = 0x19,
SubTree = 0x20,
}
```
### 5.2 Decorator Parameter Encoding
**Repeat:**
- `repeat``DecoratorRepeat` (no parameters)
- `repeat(N)``DecoratorRepeatN` + u32 count
- `repeat(min..max)``DecoratorRepeatRange` + u32 min + u32 max
**Retry:**
- `retry(N)``DecoratorRetry` + u32 max_attempts
**Timeout/Cooldown:**
- `timeout(duration)``DecoratorTimeout` + u64 milliseconds
- `cooldown(duration)``DecoratorCooldown` + u64 milliseconds
**Guard:**
- `guard(expr)``DecoratorGuard` + serialized_expression
**Binary Format Example:**
```
repeat(3) {
patrol
}
Encoded as:
[DecoratorRepeatN] [count: 3] [child_node_offset]
└─> [Action] [string_index: "patrol"]
```
---
## 6. Tree-sitter Grammar Updates
### 6.1 Updated Rules (Conceptual)
The Tree-sitter grammar will need these new patterns:
**Selector node:**
```
"selector" OR "choose" followed by "{" then one or more behavior_nodes then "}"
Optional: can have a name identifier after the keyword
```
**Sequence node:**
```
"sequence" OR "then" followed by "{" then one or more behavior_nodes then "}"
Optional: can have a name identifier after the keyword
```
**Condition node:**
```
"if" OR "when" followed by "(" then an expression then ")"
```
**Action node:**
```
identifier (optionally followed by "(" parameters ")")
No @ prefix required
```
**Decorator node:**
```
decorator_keyword (optionally followed by decorator_params) then "{" behavior_node "}"
decorator_keyword: one of the decorator keywords (repeat, retry, timeout, etc.)
decorator_params:
- "(" integer ")" for repeat(N), retry(N)
- "(" integer ".." integer ")" for repeat(min..max)
- "(" duration ")" for timeout(5s), cooldown(30s)
- "(" expression ")" for guard(condition)
```
**Subtree node:**
```
"include" followed by a path (e.g., path::to::subtree)
```
**Duration literal:**
```
integer followed by one of: d, h, m, s
Examples: 5s, 30m, 2h, 1d
```
### 6.2 Backward Compatibility (Optional) (we don't need this, new language, 1.0 lol)
If backward compatibility is required, support legacy sigils as aliases:
- `?` as alias for `selector`
- `>` as alias for `sequence`
- `*` as alias for `repeat`
- `!` as alias for `if`
- `@` prefix as optional for actions
- `@path` as alias for `include path`
**Recommendation:** Do NOT maintain backward compatibility. This is early development; clean break is better. (nope, no backwards compatibility)
---
## 7. Parser Implementation Strategy
### 7.1 AST Changes
**Current AST (storybook/src/syntax/ast.rs):**
```rust
pub enum BehaviorNode {
Selector(Vec<BehaviorNode>),
Sequence(Vec<BehaviorNode>),
Condition(Expr),
Action(String, Vec<Field>),
Decorator(String, Box<BehaviorNode>),
SubTree(Vec<String>),
}
```
**Proposed AST:**
```rust
pub enum BehaviorNode {
Selector(Vec<BehaviorNode>),
Sequence(Vec<BehaviorNode>),
Condition(Expr),
Action(String, Vec<Field>),
Decorator(DecoratorType, Box<BehaviorNode>),
SubTree(Vec<String>),
}
pub enum DecoratorType {
Repeat, // infinite
RepeatN(u32), // N times
RepeatRange(u32, u32), // min..max times
Invert,
Retry(u32), // max attempts
Timeout(Duration),
Cooldown(Duration),
Guard(Expr),
SucceedAlways,
FailAlways,
}
pub struct Duration {
pub value: u32,
pub unit: DurationUnit,
}
pub enum DurationUnit {
Days,
Hours,
Minutes,
Seconds,
}
```
### 7.2 Parser Changes
**Modify parser to:**
1. Accept `selector` | `choose` tokens instead of `?`
2. Accept `sequence` | `then` tokens instead of `>`
3. Accept `if` | `when` tokens with parenthesized expressions
4. Remove `@` prefix requirement for actions
5. Parse decorator keywords with optional parameters
6. Parse `include` keyword for subtrees
**Example parsing logic (pseudo-code):**
```rust
fn parse_behavior_node(&mut self) -> Result<BehaviorNode> {
match self.peek() {
Token::Selector | Token::Choose => self.parse_selector(),
Token::Sequence | Token::Then => self.parse_sequence(),
Token::If | Token::When => self.parse_condition(),
Token::Repeat | Token::Invert | ... => self.parse_decorator(),
Token::Include => self.parse_subtree(),
Token::Identifier => self.parse_action(),
_ => Err(ParseError::ExpectedBehaviorNode),
}
}
fn parse_decorator(&mut self) -> Result<BehaviorNode> {
let decorator_type = match self.advance() {
Token::Repeat => {
if self.match_token(Token::LParen) {
let n = self.parse_integer()?;
if self.match_token(Token::DotDot) {
let max = self.parse_integer()?;
self.expect(Token::RParen)?;
DecoratorType::RepeatRange(n, max)
} else {
self.expect(Token::RParen)?;
DecoratorType::RepeatN(n)
}
} else {
DecoratorType::Repeat
}
},
Token::Retry => {
self.expect(Token::LParen)?;
let n = self.parse_integer()?;
self.expect(Token::RParen)?;
DecoratorType::Retry(n)
},
// ... other decorators
};
self.expect(Token::LBrace)?;
let child = self.parse_behavior_node()?;
self.expect(Token::RBrace)?;
Ok(BehaviorNode::Decorator(decorator_type, Box::new(child)))
}
```
### 7.3 Lexer Changes
**Add new keyword tokens:**
```rust
// In storybook/src/syntax/lexer.rs (logos)
#[token("selector")] Selector,
#[token("choose")] Choose,
#[token("sequence")] Sequence,
#[token("then")] Then,
#[token("if")] If,
#[token("when")] When,
#[token("repeat")] Repeat,
#[token("invert")] Invert,
#[token("retry")] Retry,
#[token("timeout")] Timeout,
#[token("cooldown")] Cooldown,
#[token("guard")] Guard,
#[token("succeed_always")] SucceedAlways,
#[token("fail_always")] FailAlways,
#[token("include")] Include,
```
---
## 8. Compiler Translation
### 8.1 AST → SBIR Translation
The compiler must translate the new AST nodes to SBIR format:
```rust
fn compile_behavior_node(node: &BehaviorNode, writer: &mut BinaryWriter) -> Result<()> {
match node {
BehaviorNode::Selector(children) => {
writer.write_u8(BehaviorNodeType::Selector as u8)?;
writer.write_u32(children.len() as u32)?;
for child in children {
compile_behavior_node(child, writer)?;
}
},
BehaviorNode::Decorator(decorator_type, child) => {
match decorator_type {
DecoratorType::Repeat => {
writer.write_u8(BehaviorNodeType::DecoratorRepeat as u8)?;
},
DecoratorType::RepeatN(n) => {
writer.write_u8(BehaviorNodeType::DecoratorRepeatN as u8)?;
writer.write_u32(*n)?;
},
DecoratorType::RepeatRange(min, max) => {
writer.write_u8(BehaviorNodeType::DecoratorRepeatRange as u8)?;
writer.write_u32(*min)?;
writer.write_u32(*max)?;
},
DecoratorType::Timeout(duration) => {
writer.write_u8(BehaviorNodeType::DecoratorTimeout as u8)?;
writer.write_u64(duration.to_milliseconds())?;
},
// ... other decorators
}
compile_behavior_node(child, writer)?;
},
// ... other node types
}
Ok(())
}
```
---
## 9. Validation & Examples
### 9.1 Test Suite
**Unit tests for parser:**
```rust
#[test]
fn test_parse_selector_keyword() {
let input = r#"
behavior Test {
selector {
action_a
action_b
}
}
"#;
let ast = parse(input).unwrap();
assert!(matches!(ast.behaviors[0].root, BehaviorNode::Selector(_)));
}
#[test]
fn test_parse_repeat_decorator_with_count() {
let input = r#"
behavior Test {
repeat(3) {
patrol
}
}
"#;
let ast = parse(input).unwrap();
let root = &ast.behaviors[0].root;
assert!(matches!(root, BehaviorNode::Decorator(DecoratorType::RepeatN(3), _)));
}
#[test]
fn test_parse_nested_decorators() {
let input = r#"
behavior Test {
timeout(10s) {
retry(3) {
attempt_task
}
}
}
"#;
let ast = parse(input).unwrap();
// Assert nested structure
}
```
**Integration tests:**
- Parse all Alice in Wonderland examples with new syntax
- Compile to SBIR and validate byte-for-byte correctness
- Round-trip: parse → compile → decompile → verify
### 9.2 Migration of Existing Examples
**Alice in Wonderland migration:**
All behavior trees in `examples/alice-in-wonderland/` need updating:
1. `white_rabbit.sb` - selector/sequence conversion
2. `mad_tea_party.sb` - add `repeat` decorator (currently TODO)
3. `cheshire_cat.sb` - add timeout/cooldown decorators
4. `royal_court.sb` - add `repeat` decorators (3 TODOs)
5. `caterpillar.sb` - verify behavior tree syntax
**Migration script (optional):**
```bash
# Simple regex-based migration for basic cases
sed -i '' 's/^ ? {/ choose {/g' *.sb
sed -i '' 's/^ > {/ then {/g' *.sb
sed -i '' 's/! \(.*\)/if(\1)/g' *.sb
sed -i '' 's/@\([a-zA-Z_][a-zA-Z0-9_]*\)::\([a-zA-Z_][a-zA-Z0-9_]*\)/include \1::\2/g' *.sb
```
**Recommendation:** Migrate manually for quality and to add decorators where TODOs indicate.
---
## 10. Documentation Updates
### 10.1 Language Reference
**Update design.md Section 6:**
```markdown
### 6.1 Node Types (yep make sure the language agent is aware of these new keywords)
| Keyword | Name | Semantics |
|---------|------|-----------|
| `selector` or `choose` | Selector | Try children in order; succeed on first success |
| `sequence` or `then` | Sequence | Run children in order; fail on first failure |
| `if(expr)` or `when(expr)` | Condition | Succeed if expression evaluates to true |
| action_name | Action | Execute a named engine action |
| `repeat` | Infinite Repeat | Loop child forever |
| `repeat(N)` | Counted Repeat | Loop child N times |
| `retry(N)` | Retry Decorator | Retry child up to N times on failure |
| `timeout(duration)` | Timeout | Fail if child exceeds duration |
| `cooldown(duration)` | Cooldown | Prevent re-execution within duration |
| `guard(expr)` | Guard | Only run child if condition met |
| `invert` | Invert | Negate child's success/failure |
| `include path` | SubTree | Include behavior from another definition |
```
### 10.2 Tutorial Examples
**Add to beginner tutorial:**
```markdown
## Your First Behavior Tree
Let's create a simple behavior for a village guard who patrols and responds to threats:
```sb
behavior GuardDuty {
---description
A simple patrol behavior that responds to threats when detected.
---
choose {
// First priority: respond to threats
then {
if(threat_detected)
sound_alarm
rush_to_threat
}
// Second priority: continue patrol
repeat {
patrol_checkpoint_a
patrol_checkpoint_b
patrol_checkpoint_c
}
}
}
```
This behavior uses:
- `choose` to pick between threat response and patrol
- `then` to sequence actions in order
- `if()` to check conditions
- `repeat` to loop the patrol indefinitely
```
### 10.3 Advanced Examples
**Add complex behavior examples:**
```markdown
## Advanced: The White Rabbit's Anxiety
This complex behavior models the White Rabbit from Alice in Wonderland,
who is perpetually late and responds differently based on context:
```sb
behavior WhiteRabbit_ConstantlyLate {
choose {
// Panic when extremely late
then {
if(minutes_late > 100)
timeout(3s) {
repeat(5) {
CheckPocketWatch
MutterDesperately
}
}
SprintToDestination
}
// Drop items when startled
then {
if(obstacle_encountered)
DropGloves
DropFan
retry(2) {
FindAlternateRoute
}
}
// Extreme fear response to Queen
then {
if(queen_nearby)
FlattenEarsInFear
TremblingBow
cooldown(60s) {
AwaitCommands
}
}
// Default: perpetual anxiety
repeat {
CheckWatch
MutterAnxiously
ScurryForward
}
}
}
```
```
---
## 11. Migration Strategy
### 11.1 Breaking Changes
**This is a BREAKING change that requires:**
1. All existing `.sb` files with behaviors to be updated
2. Tree-sitter grammar update
3. Parser update
4. Compiler update
5. SBIR version bump (likely 0.1.0 → 0.2.0)
**Recommendation:** Since we're in early development (pre-1.0), make this change now before user base grows.
### 11.2 Migration Path
**Option A: Clean Break (Recommended)**
1. Implement new syntax
2. Migrate all examples manually
3. Update documentation
4. Deprecate old syntax entirely
5. Version bump to 0.2.0
**Option B: Dual Support (Not Recommended)**
1. Support both syntaxes during transition
2. Emit deprecation warnings for old syntax
3. Provide automated migration tool
4. Remove old syntax in 0.3.0
**Recommendation:** Option A. Clean break is simpler and we're early enough.
### 11.3 Migration Checklist
- [ ] Update Tree-sitter grammar with new keywords
- [ ] Update lexer to tokenize new keywords
- [ ] Update parser to handle new syntax
- [ ] Update AST to include DecoratorType enum
- [ ] Update compiler to emit correct SBIR for decorators
- [ ] Migrate all examples in `examples/alice-in-wonderland/`
- [ ] Migrate all tests in `tests/examples/`
- [ ] Update design.md documentation
- [ ] Create tutorial examples with new syntax
- [ ] Update LSP to support new keywords (coordinate with Agent 1)
- [ ] Add test suite for all decorator types
- [ ] Update SBIR specification (coordinate with Agent 5)
- [ ] Create release notes documenting breaking changes
---
## 12. Benefits Summary
### 12.1 Readability Improvements
**Before:**
```sb
? { > { ! x; @ a }; > { ! y; @ b }; @ c }
```
**After:**
```sb
choose {
then { if(x) a }
then { if(y) b }
c
}
```
**Impact:** ~300% improvement in readability for non-programmers.
### 12.2 Self-Documentation
Keywords are self-explanatory:
- "choose" tells you it's selecting between options
- "then" tells you actions happen in sequence
- "repeat(3)" tells you exactly how many times
- "timeout(5s)" tells you the duration limit
No need to memorize sigil meanings.
### 12.3 Expressiveness
New decorator support enables:
- Timed behaviors (`timeout`, `cooldown`)
- Retry logic (`retry(N)`)
- Conditional guards (`guard(condition)`)
- Counted repetition (`repeat(N)`, `repeat(min..max)`)
These are currently impossible or require workarounds.
### 12.4 Consistency with Rest of Language
The rest of Storybook uses English keywords:
- `character`, `behavior`, `life_arc`, `schedule`
- `if`, `when`, `and`, `or`
- `link`, `include`, `override`
Behavior trees should match this style.
---
## 13. Risks & Mitigations
| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| Breaking all existing `.sb` files | Certain | High | Migrate examples ourselves; provide clear migration guide |
| Parser complexity increases | Medium | Medium | Comprehensive test suite; grammar validation |
| LSP breaks during transition | Medium | High | Coordinate with Agent 1; parallel testing |
| User confusion during transition | Low | Medium | Clear documentation; examples; changelog |
| Performance impact of nested decorators | Low | Low | Benchmark; optimize if needed |
| SBIR encoding becomes verbose | Low | Medium | Efficient binary encoding; profile file sizes |
---
## 14. Success Criteria
- [ ] All behavior tree node types have keyword equivalents
- [ ] Parser handles all decorator types with parameters
- [ ] Alice in Wonderland examples validate with new syntax
- [ ] New syntax is more readable than symbolic syntax (user confirmation)
- [ ] Decorators that were TODOs are now implemented
- [ ] SBIR encoding is efficient (<10% size increase)
- [ ] LSP supports all new keywords (coordinate with Agent 1)
- [ ] Documentation is complete and beginner-friendly
- [ ] Test coverage for all keyword variants
- [ ] No regressions in compiler output
---
## 15. Next Steps
### Phase 1: Design Review
1. **Present this document to user for feedback**
2. Incorporate requested changes
3. Get explicit approval before implementation
### Phase 2: Implementation (Task #4)
1. Update Tree-sitter grammar
2. Update lexer with new tokens
3. Update parser logic
4. Extend AST with DecoratorType
5. Update compiler for SBIR generation
6. Write comprehensive test suite
7. Migrate all examples
8. Update documentation
### Phase 3: Validation
1. Run full test suite
2. Validate Alice examples end-to-end
3. Benchmark SBIR file sizes
4. Coordinate LSP testing with Agent 1
5. User acceptance testing
### Phase 4: Release
1. Create migration guide
2. Update CHANGELOG
3. Version bump to 0.2.0
4. Merge to mainline
---
## Appendix A: Complete Keyword List
**Control Flow:**
- `selector` / `choose`
- `sequence` / `then`
**Conditions:**
- `if(expr)`
- `when(expr)`
**Decorators:**
- `repeat`
- `repeat(N)`
- `repeat(min..max)`
- `invert`
- `retry(N)`
- `timeout(duration)`
- `cooldown(duration)`
- `guard(expr)`
- `succeed_always`
- `fail_always`
**Subtrees:**
- `include path`
**Actions:**
- No keyword, just action name
**Duration Units:**
- `d` (days)
- `h` (hours)
- `m` (minutes)
- `s` (seconds)
---
## Appendix B: Grammar Comparison
**Current Grammar (Symbolic):**
```
behavior_node can be:
- selector_node using ? sigil
- sequence_node using > sigil
- repeat_node using * sigil
- action_node (identifier or @ identifier)
- subtree_node (@path::to::subtree)
```
**Proposed Grammar (Keywords):**
```
behavior_node can be:
- selector_node using "selector" or "choose" keyword
- sequence_node using "sequence" or "then" keyword
- condition_node using "if(expr)" or "when(expr)"
- decorator_node using decorator keywords with optional params
- action_node (just identifier, no prefix)
- subtree_node using "include path"
```
---
## Appendix C: SBIR Binary Encoding Examples
**Selector with two children:**
```
[0x01] // Selector
[0x02 0x00 0x00 0x00] // 2 children
[0x04] [0x05 0x00 0x00 0x00] // Action "patrol" (string index 5)
[0x04] [0x06 0x00 0x00 0x00] // Action "rest" (string index 6)
```
**Repeat(3) decorator:**
```
[0x11] // DecoratorRepeatN
[0x03 0x00 0x00 0x00] // Count: 3
[0x04] [0x07 0x00 0x00 0x00] // Action "knock" (string index 7)
```
**Timeout(5s) with nested action:**
```
[0x15] // DecoratorTimeout
[0x88 0x13 0x00 0x00 0x00 0x00 0x00 0x00] // 5000 milliseconds
[0x04] [0x08 0x00 0x00 0x00] // Action "wait_for_response" (string index 8)
```
---
**END OF DESIGN DOCUMENT**