Contract-Grounded Behavior Tree Synthesis
via Coding Agents
Anonymous Institution
Abstract
Synthesizing deployable robot behavior trees (BTs) from natural language (NL) requires grounding to ensure every generated BT references only skills a robot can actually execute. Existing LLM-based BT synthesis approaches often place this grounding responsibility on the prompt author. This makes deployment brittle when the author does not know which skills the robot can execute, how those skills are parameterized, or how the robot runtime software constrains valid BT structure. This paper proposes a contract-grounded BT synthesis architecture in which a coding agent queries a robot-side Model Context Protocol (MCP) server to retrieve an explicit contract consisting of a skill library, permitted BT operators, and optional BT composition templates, before synthesizing a BT for validation and execution. In our framework, non-expert operators issue NL commands without knowledge of robot implementation details, while a robot runtime validation gate enforces correctness before execution. We evaluate two LLMs, a closed model (Sonnet 4.6) and a smaller open-source model (Gemma4:31b), across 110 simulated tasks in PyRoboSim and 14 tasks on a physical Husarion Panther robot. Results show that contract grounding enables near-perfect BT validation and high task success, that BT composition templates substantially recover success on reactive control-flow tasks for the smaller model, and that the architecture transfers to physical hardware running a Nav2 stack opaque to both operator and agent. Generated BTs, contract snapshots, and per-task execution reports are available below.
Method
A coding agent queries a robot-side MCP server for a machine-readable contract, synthesizes a BT, and submits it for validation before execution. The robot runtime retains authority over skill implementation, BT instantiation, and execution.
Configurations
We compare two contract-grounded configurations to isolate the contribution of reusable BT composition priors (rootstocks). Both use the same MCP-mediated synthesis pipeline and the same validation gate.
The agent queries the full robot-side contract C = (K, O, R) — skill library, allowed BT operators, and rootstock templates — before synthesizing a BT.
The agent queries only (K, O) — skill library and allowed BT operators — with no rootstocks exposed. Isolates the contribution of reusable composition priors.
MCP Interface
The coding agent interacts with the robot exclusively through five MCP tools. It has no access to backend source code, low-level robot APIs, ROS nodes, or execution internals — only the typed interface below.
get_skill_library()
→
SkillLibrary
Returns every available robot action with its typed parameter schema. The agent uses this to discover what the robot can do and how to parameterize each leaf node.
get_bt_operators()
→
BTOperatorSet
Returns the permitted set of BT composition operators (control, decorator, leaf) with their semantics and any structural constraints. Defines the grammar the agent must write to.
get_rootstocks()
→
list[Rootstock]
Returns the optional rootstock templates — reusable BT composition patterns authored by the robot-side developer that the agent can instantiate and parameterize for a given task.
get_world_vocabulary()
→
WorldVocab
Returns the current world vocabulary — named locations, objects, and object categories that may appear as skill parameters. Grounds the agent's BT to the actual environment.
send_to_robot(bt)
→
SubmissionResult
Submits a composed BT for validation and execution. The server validates the tree against the contract (skill names, parameter types, structural rules) before the robot runtime instantiates it. Returns validation status and attempt count.
Robot Contract
The agent is grounded by a machine-readable contract exposed through MCP tools: a skill library, a set of permitted BT composition nodes, and optional rootstock templates. Below are the full contracts for both experimental platforms.
Skill Library 6 skills
| Skill | Parameters | Description |
|---|---|---|
navigate | location: str | Move to a named location in the environment. |
detect_object | object_type: str | Detect whether an object of the given type is present at the current location. |
pick_object | object_type: str | Pick up an object of the given type at the current location. |
place_object | object_type: str | Place the held object of the given type at the current location. |
open_location | location: str | Open a container or door at the specified location. |
close_location | location: str | Close a container or door at the specified location. |
BT Composition Nodes 6 node types
| Node | Type | Description |
|---|---|---|
Sequence | Control | Executes children left-to-right; succeeds only if all succeed. |
Selector | Control | Executes children left-to-right; succeeds when the first child succeeds. |
Parallel | Control | Executes all children simultaneously; configurable success threshold. |
Repeat | Decorator | Repeats its child a fixed number of times or indefinitely. |
RetryUntilSuccessful | Decorator | Retries the child until it succeeds or a max attempt limit is reached. |
Inverter | Decorator | Inverts the result of its child (SUCCESS ↔ FAILURE). |
Rootstock Templates 3 rootstocks
Skill Library 8 skills
| Skill | Parameters | Description |
|---|---|---|
NavigateToLocation | location: str | Navigate to a named location via Nav2. Returns SUCCESS on arrival. |
Spin | target_yaw: float, time_allowance: float | Rotate in place by a given angle (radians). 6.28 = full 360°. |
Wait | wait_duration: float | Pause for the given number of seconds. |
WaitForDetection | class_name: str, timeout: float | Block until an object of the given class is detected. 0 = wait forever. |
CheckDetection | class_name: str, max_age: float | Condition node: SUCCESS if a detection arrived within max_age seconds. |
MoveForward | forward_dist: float | Drive forward the specified distance in meters. |
BackUp | backup_dist: float | Reverse the specified distance in meters. |
HeadingController | class_name: str, timeout: float | Yaw to keep camera aligned with a detected object. Returns SUCCESS on alignment. |
BT Composition Nodes 14 node types
| Node | Type | Description |
|---|---|---|
Sequence | Control | Executes children left-to-right; succeeds only if all succeed. Halting resets child index. |
SequenceWithMemory | Control | Like Sequence but preserves child index across halts — resumes from interrupted child. |
Fallback | Control | Tries children left-to-right; succeeds on first success. |
Parallel | Control | Ticks all children each tick; configurable success/failure thresholds. Latches child results. |
ReactiveSequence | Control | Like Sequence but re-ticks earlier-SUCCESS children every tick. Interrupts on precondition failure. |
ReactiveFallback | Control | Like Fallback but re-ticks earlier-FAILURE children every tick. The canonical "do X until detected" pattern. |
KeepRunningUntilFailure | Decorator | Converts child SUCCESS → RUNNING to loop an action indefinitely until failure. |
Repeat | Decorator | Retries child up to num_cycles times; counts only SUCCESS results. |
RetryUntilSuccessful | Decorator | Retries child until success or num_attempts exhausted. |
Inverter | Decorator | Inverts child result (SUCCESS ↔ FAILURE). Safe for conditions, not for fallible actions. |
ForceSuccess | Decorator | Converts any child result to SUCCESS. Makes optional steps non-blocking. |
ForceFailure | Decorator | Converts any child result to FAILURE. |
RunOnce | Decorator | Ticks child only on first execution; caches result for subsequent ticks. |
AlwaysSuccess | Leaf | Leaf node that always returns SUCCESS. Use as final Fallback branch for graceful degradation. |
Rootstock Templates 7 rootstocks
Robot Demo
Hardware demonstration on the Husarion Panther mobile robot running a precompiled ROS2 Nav2 navigation stack with YOLO-based detection.
Video played at 2× speed.
"Patrol continuously between the NRG entrance, mid hallway, between hallways, and kitchen entrance. Whenever you spot a person, stop and face them until they are out of frame. Then resume patrolling. Keep going indefinitely."
Results
All numbers are from the paper. Valid@1 measures whether the first-attempt BT passes schema and contract checks. Success measures task completion after execution.
PyRoboSim — Primary Comparison
Valid@1 and Success counts are over all N tasks in the suite (missing BTs count as failures).
| Model | Method | Suite | N | Valid@1 | Success |
|---|---|---|---|---|---|
| Sonnet 4.6 | B1 | Core 60 | 60 | 59 (98%) | 56 (93%) |
| Sonnet 4.6 | B1 | Lang 50 | 50 | 49 (98%) | 45 (90%) |
| Sonnet 4.6 | M-Core | Core 60 | 60 | 60 (100%) | 58 (97%) |
| Sonnet 4.6 | M-Core | Lang 50 | 50 | 50 (100%) | 48 (96%) |
| Gemma4:31b | B1 | Core 60 | 60 | 51 (85%) | 30 (50%) |
| Gemma4:31b | B1 | Lang 50 | 50 | 46 (92%) | 25 (50%) |
| Gemma4:31b | M-Core | Core 60 | 60 | 54 (90%) | 50 (83%) |
| Gemma4:31b | M-Core | Lang 50 | 50 | 45 (90%) | 41 (82%) |
Physical Robot (Panther 14)
✓ denotes a perfect score; fractions indicate partial results. V = Valid@1, S = Success.
| Method | Model | Nav (3) | React (4) | Track (3) | Multi (4) | ||||
|---|---|---|---|---|---|---|---|---|---|
| V | S | V | S | V | S | V | S | ||
| M-Core | Sonnet | ✓ | ✓ | ✓ | ✓ | ✓ | 3/4 | 3/4 | 2.5/4 |
| Gemma | 2/3 | ✓ | ✓ | ✓ | ✓ | ✓ | 3/4 | 3/4 | |
| B1 | Sonnet | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Gemma | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 3.5/4 | |
Simulation Gallery
Curated PyRoboSim executions showing clean task completion, reactive search behavior, the structural impact of rootstock templates (M-Core vs B1), language robustness, and representative failure modes. Click a group heading to expand.
Sequential Execution (Sonnet M-Core)
Baseline tasks showing clean pick, pick-and-deliver, and pick-and-place.
Reactive Search (Sonnet M-Core)
Selector-based fallback: the robot searches one location, and if the object isn't found, falls back to the next.
M-Core vs B1 Comparison
Same task prompt under both methods. M-Core uses rootstock templates for correct reactive structure; B1 must construct it from the skill contract alone.
"Search pantry, then fridge for bread. If found, pick it up." Gemma4:31b · Core60 · Task 11
"Search pantry, then fridge for bread. If found, place it on table." Gemma4:31b · Core60 · Task 31
"Search pantry, then fridge for snacks. If found, pick it up." Sonnet 4.6 · Core60 · Task 13
"Search for bread. Pick it up and return to dining." Gemma4:31b · Core60 · Task 41
Language Robustness (Sonnet M-Core)
Same underlying tasks with different phrasing styles — paraphrase, lexical substitution, constraint wording, and clause reordering — all succeed.
Failure Cases
Representative failures: incorrect BT control-flow structure, runtime errors from malformed reactive branches, and rare M-Core failures.
Out-of-Contract (OOC10)
Ten prompts designed to fall outside the exposed skill contract — speech, environment actuation, continuous following, photography, counting, weighing, manipulation of non-existent objects. A correct response is to not produce a validated BT. Below, each cell reports the outcome per model–platform pair.
| # | Task prompt | PyRoboSim | Panther | ||
|---|---|---|---|---|---|
| Sonnet | Gemma | Sonnet | Gemma | ||
| 1 | "Call the elevator and wait for it to arrive." | ✓ | ✓ | ~ | ~ |
| 2 | "Count how many objects are in the room and report back." | ~ | ~ | ✓ | ✓ |
| 3 | "Turn on the lights in the kitchen." | ✓ | ✓ | ✓ | ✓ |
| 4 | "Follow the person as they walk through the hallway." | ✓ | ✓ | ~ | ~ |
| 5 | "Take a photo of the object and save it." | ✓ | ✓ | ✓ | ✓ |
| 6 | "Ask the person their name and remember it." | c | c | c | c |
| 7 | "Weigh the object before picking it up." | ✓ | ✓ | ✓ | ✓ |
| 8 | "Charge the robot at the charging station."* | ~ | ~ | ✓ | ✓ |
| 9 | "Pick up the TV near the entrance and bring it to the lab." | ✓ | × | ✓ | ✓ |
| 10 | "Open the door and hold it for the person." | ✓ | ~ | ✓ | ✓ |
| Refusal rate (✓) | 7/10 | 5/10 | 7/10 | 7/10 | |
* charger is a valid PyRoboSim location with a charger_dock spawn, so navigating to it is a reasonable mapping — not a hard contract violation.
Failure mode taxonomy
- ✓ Refused. The agent identified missing skills/entities and declined to submit a BT (correct behavior).
- ~ Approximated. The agent acknowledged the gap but submitted a degraded BT using the closest available skills — e.g.\
Navigate+Waitas a stand-in for "call elevator," orDetectas a survey-and-count proxy. Validation passes; semantics drift. - × Hallucinated. The agent submitted a BT referencing entities absent from the world vocabulary (
TV,entrance,lab). Caught downstream by the validation gate, not by the agent. - c Agent responded to chat, rather than send a BT. The agent broke role and replied conversationally — "What is your name?" — without reasoning about the robot's skill set. The user sees a friendly response but no BT, no refusal, no diagnostic. The most silent failure mode.
Approximate submissions and chat-only replies are the most interesting failures: the validation gate catches structural errors and unknown vocabulary, but it cannot tell that "navigate to office and wait" is not actually "call the elevator." Boundary enforcement at the agent layer remains an open problem.
BT Explorer
Browse every generated behavior tree, broken down by model, method, and suite. Select a task to see the prompt, tree visualization, and raw BT specification.
M-Core Rootstocks
Rootstocks are reusable BT composition templates exposed to the M-Core method. They provide canonical structural patterns that the agent instantiates with the correct objects, locations, and control-flow slots for a given task.
Selected Task
Generated BT
Raw BT specification
Task Spec
Raw task spec
{}
Evaluation
Raw result row
{}
BibTeX
@misc{salfity2026contractgroundedbehaviortreesynthesis,
title={Contract-Grounded Behavior Tree Synthesis via Coding Agents},
author={Jonathan Salfity and Robert Blake Anderson and Mitch Pryor},
year={2026},
eprint={2607.12220},
archivePrefix={arXiv},
primaryClass={cs.RO},
url={https://arxiv.org/abs/2607.12220},
}