BREAKING CHANGES: - Relationship syntax now requires blocks for all participants - Removed self/other perspective blocks from relationships - Replaced 'guard' keyword with 'if' for behavior tree decorators Language Features: - Add tree-sitter grammar with improved if/condition disambiguation - Add comprehensive tutorial and reference documentation - Add SBIR v0.2.0 binary format specification - Add resource linking system for behaviors and schedules - Add year-long schedule patterns (day, season, recurrence) - Add behavior tree enhancements (named nodes, decorators) Documentation: - Complete tutorial series (9 chapters) with baker family examples - Complete reference documentation for all language features - SBIR v0.2.0 specification with binary format details - Added locations and institutions documentation Examples: - Convert all examples to baker family scenario - Add comprehensive working examples Tooling: - Zed extension with LSP integration - Tree-sitter grammar for syntax highlighting - Build scripts and development tools Version Updates: - Main package: 0.1.0 → 0.2.0 - Tree-sitter grammar: 0.1.0 → 0.2.0 - Zed extension: 0.1.0 → 0.2.0 - Storybook editor: 0.1.0 → 0.2.0
33 KiB
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:
?→selectororchoose>→sequenceorthen*→repeat(with optional parameters)~→ Decorator keywords:invert,retry(N),timeout(duration),cooldown(duration),guard(condition)!→iforwhen(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:
behavior WhiteRabbit_ConstantlyLate {
? {
> {
CheckPocketWatch
RealizeHowLate
MutterAnxiously
ScurryToDestination
}
> {
EncounterObstacle
DropGloves
DropFan
PanicFurther
}
}
}
Current Limitations Identified:
- Decorator syntax missing: Multiple TODOs in examples mention "Add repeater decorator support"
- Sigils are cryptic:
?and>are not self-documenting - No parameterized decorators: Cannot express
repeat(3),timeout(5s),retry(2) - Condition syntax unclear:
!looks like negation rather than "if this condition" - 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_nodewith*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:
// 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:
// 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:
// 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:
// 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:
// 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
// Equivalent to current * sigil
repeat {
patrol
}
repeat(N) - Loop N times
repeat(3) {
knock_on_door
}
repeat(min..max) - Loop between min and max times
repeat(2..5) {
search_area
}
invert - Negate child result
invert {
if(enemy_nearby)
}
retry(N) - Retry up to N times on failure
retry(3) {
attempt_connection
}
timeout(duration) - Fail if child exceeds duration
timeout(5s) {
wait_for_response
}
cooldown(duration) - Prevent re-execution within duration
cooldown(30s) {
shout_warning
}
guard(condition) - Only run child if condition met
guard(energy > 50) {
sprint_to_safety
}
succeed_always - Always return success
succeed_always {
attempt_optional_task
}
fail_always - Always return failure (useful for debugging)
fail_always {
disabled_behavior
}
3. Complete Grammar Specification
3.1 Behavior Block Syntax
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:
timeout(10s) {
retry(3) {
attempt_difficult_task
}
}
Inline conditions within control flow:
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.)
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:
behavior WhiteRabbit_ConstantlyLate {
? {
> {
CheckPocketWatch
RealizeHowLate
MutterAnxiously
ScurryToDestination
}
> {
EncounterObstacle
DropGloves
DropFan
PanicFurther
ReverseDirection
}
> {
SpotQueen
FlattenEarsInFear
TremblingBow
AwaitCommands
}
> {
CheckWatch
RunInCircles
CheckWatchAgain
}
}
}
After:
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):
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):
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:
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:
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
// 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 countrepeat(min..max)→DecoratorRepeatRange+ u32 min + u32 max
Retry:
retry(N)→DecoratorRetry+ u32 max_attempts
Timeout/Cooldown:
timeout(duration)→DecoratorTimeout+ u64 millisecondscooldown(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 forselector>as alias forsequence*as alias forrepeat!as alias forif@prefix as optional for actions@pathas alias forinclude 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):
pub enum BehaviorNode {
Selector(Vec<BehaviorNode>),
Sequence(Vec<BehaviorNode>),
Condition(Expr),
Action(String, Vec<Field>),
Decorator(String, Box<BehaviorNode>),
SubTree(Vec<String>),
}
Proposed AST:
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:
- Accept
selector|choosetokens instead of? - Accept
sequence|thentokens instead of> - Accept
if|whentokens with parenthesized expressions - Remove
@prefix requirement for actions - Parse decorator keywords with optional parameters
- Parse
includekeyword for subtrees
Example parsing logic (pseudo-code):
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:
// 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:
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:
#[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:
white_rabbit.sb- selector/sequence conversionmad_tea_party.sb- addrepeatdecorator (currently TODO)cheshire_cat.sb- add timeout/cooldown decoratorsroyal_court.sb- addrepeatdecorators (3 TODOs)caterpillar.sb- verify behavior tree syntax
Migration script (optional):
# 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:
### 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:
## 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:
chooseto pick between threat response and patrolthento sequence actions in orderif()to check conditionsrepeatto 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:
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,scheduleif,when,and,orlink,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
- Present this document to user for feedback
- Incorporate requested changes
- Get explicit approval before implementation
Phase 2: Implementation (Task #4)
- Update Tree-sitter grammar
- Update lexer with new tokens
- Update parser logic
- Extend AST with DecoratorType
- Update compiler for SBIR generation
- Write comprehensive test suite
- Migrate all examples
- Update documentation
Phase 3: Validation
- Run full test suite
- Validate Alice examples end-to-end
- Benchmark SBIR file sizes
- Coordinate LSP testing with Agent 1
- User acceptance testing
Phase 4: Release
- Create migration guide
- Update CHANGELOG
- Version bump to 0.2.0
- Merge to mainline
Appendix A: Complete Keyword List
Control Flow:
selector/choosesequence/then
Conditions:
if(expr)when(expr)
Decorators:
repeatrepeat(N)repeat(min..max)invertretry(N)timeout(duration)cooldown(duration)guard(expr)succeed_alwaysfail_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