Mecha10 Behavior Runtime
A unified behavior composition system for robotics and AI that enables building complex robot behaviors from simple, composable pieces.
Overview
The behavior runtime provides a tick-based execution model where all behaviors implement a single BehaviorNode trait. This creates a uniform interface for:
- Custom Rust behaviors (complex logic, high performance)
- AI inference nodes (YOLO, LLMs, RL policies)
- Planning and navigation (A*, RRT, SLAM)
- Composition primitives (Sequence, Selector, Parallel)
Quick Start
use *;
// Define a custom behavior
;
// Execute it
async
Core Concepts
BehaviorNode Trait
All behaviors implement this single trait:
Node Status
Behaviors return one of three statuses:
NodeStatus::Success- Behavior completed successfullyNodeStatus::Failure- Behavior failed (irrecoverable error)NodeStatus::Running- Behavior is still executing
Composition Primitives
Sequence Node
Executes children in order until one fails or all succeed.
let sequence = new;
Selector Node (Fallback)
Tries children until one succeeds or all fail.
let selector = new;
Parallel Node
Executes all children concurrently.
let parallel = new;
Node Registry & Loading
The behavior runtime provides a powerful system for loading behavior trees from JSON configurations. This enables configuration-driven behavior and separation between behavior logic (Rust) and composition (JSON). Hot-reloading support is planned for future releases (see Hot Reload Status below).
Registering Custom Nodes
Create a NodeRegistry and register your custom behavior types:
use *;
// Define your custom behavior
// Register it with the registry
let mut registry = new;
registry.register;
Loading Behavior Trees
Use the BehaviorLoader to load behavior trees from JSON:
// Create a loader with your registry
let loader = new;
// Option 1: Load from a JSON string
let json = r#"{
"name": "my_behavior",
"root": {
"type": "node",
"node": "move_to_goal",
"config": { "speed": 2.0 }
}
}"#;
let behavior = loader.load_from_json?;
// Option 2: Load from a file
let behavior = loader.load_from_file?;
// Option 3: Load from a parsed config
let config = from_file?;
let behavior = loader.load?;
Complete Workflow
Here's the complete workflow from registration to execution:
// 1. Create registry and register nodes
let mut registry = new;
registry.register;
registry.register;
// 2. Create loader and load behavior tree
let loader = new;
let behavior = loader.load_from_file?;
// 3. Create executor and run
let ctx = new.await?;
let mut executor = new;
executor.init.await?;
let = executor.run_until_complete.await?;
println!;
See examples/load_and_execute.rs for a complete working example.
JSON Configuration
Behaviors can be defined in JSON for dynamic composition:
Load and execute:
// Step 1: Create a registry and register your node types
let mut registry = new;
registry.register;
registry.register;
registry.register;
// Step 2: Create a loader and load the behavior tree
let loader = new;
let behavior = loader.load_from_file?;
// Step 3: Execute with the behavior executor
let mut executor = new;
let = executor.run_until_complete.await?;
Built-in Action Nodes
The runtime includes several built-in action nodes for common robotics tasks. Register them with:
use register_builtin_actions;
let mut registry = new;
register_builtin_actions;
WanderNode
Generates random movement commands for exploration:
Parameters:
topic: Topic to publish velocity commands (required)linear_speed: Forward speed in m/s (default: 0.3)angular_speed: Max turning speed in rad/s (default: 0.5)change_interval_secs: Seconds between direction changes (default: 3.0)
Behavior: Publishes constant linear velocity with periodically changing random angular velocity. Returns Running continuously.
MoveNode
Executes a fixed velocity command for a specified duration:
Parameters:
topic: Topic to publish velocity commands (required)linear: Forward/backward velocity in m/s (required)angular: Turning velocity in rad/s (required)duration_secs: Duration to execute (optional, null = forever)
Behavior: Publishes the specified velocity command. Returns Running while executing, Success when duration elapses. If no duration specified, runs forever.
TimerNode
Waits for a specified duration before succeeding:
Parameters:
duration_secs: Duration to wait in seconds (required)
Behavior: Returns Running until the duration elapses, then returns Success. Useful for delays in sequences.
SensorCheckNode
Subscribes to a sensor topic and evaluates a condition against the latest reading:
Parameters:
topic: Sensor topic to subscribe to (required)field: Field name in sensor message (required)operator: Comparison operator -less_than,greater_than,equal(required)threshold: Comparison value (required)
Behavior: Lazily subscribes to topic on the first tick. Each tick, drains any pending
messages and keeps the most recent one - if no new message has arrived since the last tick, the
previously received reading is reused, since a publisher is not required to publish on every
tick. Returns Running until a first sensor reading has been received. Once a reading is
available, returns Failure if field is missing or not numeric on that reading, otherwise
compares field's value against threshold using operator and returns Success if the
comparison holds, Failure otherwise.
Seed Templates
The package includes production-ready behavior tree templates in the seeds/ directory:
- patrol_simple.json - Square patrol loop using timed movements
- idle_wander.json - Random wandering for exploration and idle movement
You can use these templates as starting points for your own behavior trees or load them directly:
let loader = new;
let behavior = loader.load_from_file?;
Creating Behavior Trees with the CLI
The Mecha10 CLI provides an interactive wizard for creating behavior trees:
# Interactive mode - wizard will guide you through template selection
# Create with specific template
# List available templates
# Validate an existing behavior tree
Configuration Validation
The runtime provides comprehensive validation for behavior trees before execution:
use ;
use json;
let mut registry = new;
register_builtin_actions;
let config = json!;
let result = validate_behavior_config?;
if !result.valid
for warning in &result.warnings
Validation Checks:
- Required fields (
name,root) - Node types are registered or are composition nodes
- Tree structure is well-formed
- Children arrays exist for composition nodes
- Schema reference is present (warning if missing)
- Empty children arrays (warning)
ValidationResult:
Configuration Resolution
Behavior configs resolve through a 2-tier framework/project config.json lookup rooted at
configs/behaviors/ (see mecha10_cli_core::paths::config::behavior_config_candidates). Highest
precedence first:
- Project (highest):
configs/behaviors/wander/config.json- project-committed override - Framework (lowest):
configs/behaviors/@mecha10/wander/config.json- bundled framework default
There is no @local tier for behaviors (LAB-1990) - unlike node configs, nothing gitignores it
and it syncs like any other config via mecha10 config push. Behaviors moved from a top-level
behaviors/ directory to configs/behaviors/ in LAB-2072: mecha10 config push/pull/list
only ever walk configs/, so the top-level behaviors/ directory was never actually reachable by
them despite this doc (and LAB-1990's own justification for dropping @local) claiming it was -
this move makes that claim true. Push and pull both work correctly for behaviors: every config
key is prefixed with its category root (behaviors/... vs nodes/...), so a pulled behavior
config lands back under configs/behaviors/... rather than being mistaken for a node config -
see mecha10_robot_config::sync::derive_config_key/write_configs_to_dir's doc comments.
Resolution picks the highest-precedence tier that exists on disk - whole files are swapped, not deep-merged (LAB-1990).
Loading a Behavior Config
use ;
// Auto-detect project root (walks up to find mecha10.json)
let project_root = detect_project_root?;
// Resolves and loads the highest-precedence tier present on disk
let config = load_behavior_config.await?;
Example: Overriding a Behavior
Framework default (configs/behaviors/@mecha10/wander/config.json):
Project override (configs/behaviors/wander/config.json) - a complete, valid behavior definition,
not just the fields being changed:
Result: the project override at configs/behaviors/wander/config.json exists, so it wins outright -
the robot moves slowly (0.1 m/s linear) regardless of what configs/behaviors/@mecha10/wander/config.json
contains.
Composition Patterns
Pattern 1: Sequential Execution (Sequence Node)
Use sequences when tasks must be performed in order, and all must succeed:
Behavior: Executes children left-to-right. If any child fails, the sequence fails immediately. All children must succeed for the sequence to succeed.
Use cases: Multi-step tasks, patrol patterns, initialization sequences
Pattern 2: Fallback/Priority (Selector Node)
Use selectors when you have multiple strategies and want to try them in priority order:
Behavior: Tries children left-to-right. If a child succeeds, the selector succeeds immediately. If all children fail, the selector fails.
Use cases: Fallback strategies, priority-based behavior selection, fault tolerance
Pattern 3: Concurrent Execution (Parallel Node)
Use parallel nodes when tasks can run simultaneously:
Behavior: Executes all children concurrently. Success/failure depends on the policy:
require_all: All children must succeedrequire_one: At least one child must succeedrequire_n(N): At least N children must succeed
Use cases: Monitoring + action, sensor fusion, multi-tasking
Pattern 4: Subsumption Architecture
Illustrative only. This tree is a composition-shape example, not a copy-pasteable config - node types like
detect_emergencyandemergency_stopare not registered byregister_builtin_actionsand aren't implemented anywhere in the codebase. The only built-in action nodes today arewander,move,timer, andsensor_check(see Built-in Action Nodes); a real subsumption tree would need custom nodes registered for the missing types before it would load.
Layered behavior with priority suppression (high-priority behaviors override low-priority ones):
Use cases: Safety-critical systems, reactive robotics, behavior hierarchies
See packages/behavior-patterns/seeds/safety_subsumption.json for a complete example.
Pattern 5: Ensemble/Fusion
Illustrative only. This tree is a composition-shape example, not a copy-pasteable config - node types like
yolo_detector,mobilenet_detector,depth_detector, andfuse_detectionsare not registered byregister_builtin_actionsand aren't implemented anywhere in the codebase. The only built-in action nodes today arewander,move,timer, andsensor_check(see Built-in Action Nodes); a real ensemble/fusion tree would need custom nodes registered for the missing types before it would load.
Multiple models or sensors providing input for a single decision:
Use cases: Sensor fusion, multi-model AI, robust perception
See packages/behavior-patterns/seeds/multi_model_ensemble.json for a complete example.
Execution Engine
The BehaviorExecutor manages tick-based execution:
let executor = new // 30 Hz tick rate
.with_max_ticks; // Optional timeout
// Run until completion
let = executor.run_until_complete.await?;
// Or run for a fixed duration
let = executor.run_for_duration.await?;
Execution statistics:
println!;
println!;
println!;
println!;
Architecture
Philosophy
- Behavior Logic = Rust code (complex logic, high performance)
- Behavior Composition = JSON config (simple orchestration, will support hot-reloading)
- Configuration = JSON with validation (parameters only)
Benefits
- Unified Interface: Everything is a BehaviorNode
- Composable: Mix and match behaviors freely
- Type-Safe: Rust's type system ensures correctness
- Performance: Zero-cost abstractions, compiled Rust
- Debuggable: Clear execution model with stats
Integration with Mecha10
This package is part of the Mecha10 framework and integrates with:
- mecha10-core: Provides
Contextfor messaging and state - AI nodes (planned): YOLO, LLMs, RL policies
- Planning nodes (planned): A*, RRT, SLAM
- Dashboard: Real-time behavior visualization
Status
Current Status: Phase 5 Complete - Config Loading & Validation ✅
Implemented:
- ✅ Core
BehaviorNodetrait - ✅
NodeStatusenum - ✅ Composition primitives (Sequence, Selector, Parallel)
- ✅ JSON configuration types with JSON Schema
- ✅ Tick-based execution engine
- ✅ Execution statistics
- ✅ Node registry for dynamic node instantiation
- ✅ Behavior loader for loading trees from JSON
- ✅ Integration examples and documentation
- ✅ Production-ready seed templates (6 templates)
- ✅ CLI wizard for creating behavior trees
- ✅ Comprehensive composition pattern documentation
- ✅ JSON Schema for validation and IDE support
- ✅ Built-in action nodes (WanderNode, MoveNode, TimerNode, SensorCheckNode)
- ✅ Configuration validation (validate_behavior_config with detailed errors/warnings)
- ✅ 2-tier configuration resolution (framework default → project override)
- ✅ BehaviorExecutor node (mecha10-nodes-behavior-executor for integration)
- ✅ Integration tests (14 tests for action nodes and config validation)
Next Steps (Priority 7 - see TODOS.md):
- Behavior tree catalog service (REST API + PostgreSQL)
- AI node library (Vision, Language, Speech)
- Planning nodes (Path planning, SLAM, OpenCV)
- Dashboard monitoring interface
- Hot-reload support for behavior trees
Hot Reload Status
Current: The behavior runtime supports loading behavior trees from JSON files at startup. You can modify JSON files and restart your nodes to load updated behavior trees.
Planned: Automatic hot-reloading of behavior trees while nodes are running. This will enable:
- File system watching for JSON behavior tree changes
- Validation of new behavior trees before applying
- Seamless tree swapping without node restart
- State preservation for stateful nodes (optional)
- Rollback on reload errors
Timeline: Hot-reload functionality is tracked in TODOS.md under P2 (Medium Priority) tasks.
Current Workflow:
- Edit your behavior tree JSON file
- Stop your running node (Ctrl+C)
- Restart the node with
mecha10 run <node-name> - The node will load the updated behavior tree
Testing
The package currently builds successfully. Integration tests require a running Redis instance (provided by mecha10-core Context).
License
MIT
See Also
- TODOS.md - Priority 7: AI Native