> ## Documentation Index
> Fetch the complete documentation index at: https://docs.innate.bot/llms.txt
> Use this file to discover all available pages before exploring further.

# Composing Skills (pre-release)

<Warning>
  Composing skills is a **pre-release feature**. The `innate.skills` calling
  convention shown here is not part of a stable OS build yet, so the import path,
  the result shape, and `SkillFailed` behavior may change without notice. Treat
  this page as a preview, not a stable reference.
</Warning>

A skill can call other skills. Import them from `innate.skills` and call them like
ordinary functions — the runtime runs each one to completion, then returns control to
your code. This lets you build a high-level behavior out of the capabilities you (and
the platform) already have, without re-implementing navigation, manipulation, or speech.

## The idea

Every shipped and custom skill is exposed as a callable in `innate.skills`. Inside your
own `execute()`, calling one:

* **Blocks** until the sub-skill finishes — no callbacks or polling.
* **Raises `SkillFailed`** if the sub-skill fails, so you can `try`/`except` around it.
* **Returns a result** whose `.data` holds the sub-skill's typed output.
* **Shows up as its own step** in the app timeline, so a composed routine is legible
  while it runs.

Learned policies and scripted skills share the exact same call shape — `pick_socks()`
(an ACT policy) is called the same way as `turn_in_place()` (scripted). The caller
doesn't need to know how the sub-skill is implemented.

## A worked example

This is `run_routine_demo`, a shipped skill you can read in full at
`~/innate-os/workspace/innate_skills/run_routine_demo.py`. It talks, emotes, shuffles
back and forth, turns, and attempts a learned pick — chaining six different skills.

```python theme={null}
from innate import RobotState, RobotStateType, Skill, SkillFailed, SkillResult
from innate.skills import (
    arm_zero_position,
    head_emotion,
    move_straight,
    pick_socks,
    turn_in_place,
)


class RunRoutineDemo(Skill):
    """Demo of a chained routine: skills are imported functions, calls block,
    failures raise SkillFailed, and each call is its own step in the app."""

    battery = RobotState(RobotStateType.LAST_BATTERY)

    @property
    def name(self):
        return "run_routine_demo"

    def guidelines(self):
        return (
            "Run the demo routine: talk, emote, shuffle, turn, and try to pick a "
            "sock. Use when the user asks for the demo."
        )

    def execute(self):
        runs = self.storage.get("runs", 0) + 1
        self.storage["runs"] = runs

        arm_zero_position()
        head_emotion(emotion="excited")
        self.say(f"Demo number {runs}. Watch this.", wait=True)

        for distance in (0.2, -0.2):
            move_straight(distance=distance)

        turn = turn_in_place(angle_degrees=90)
        self.say(f"I turned {turn.data.turned_degrees:.0f} degrees.")
        turn_in_place(angle_degrees=-90, timeout=20)

        try:
            pick_socks(timeout=60)  # learned policy, same call shape
        except SkillFailed:
            head_emotion(emotion="disappointed")
            self.say("No socks today.")

        if self.battery:
            self.say(f"Battery at {self.battery['percentage']:.0%}.")
        head_emotion(emotion="proud")
        self.say("All done!")
        return "Demo complete", SkillResult.SUCCESS
```

## What each piece is doing

**Import skills as functions.** `from innate.skills import arm_zero_position, …` pulls
in the skills you want to chain. Anything installed on the robot — shipped or custom —
is available here.

**Pass parameters as keyword arguments.** A sub-skill's parameters are just function
arguments: `move_straight(distance=distance)`, `turn_in_place(angle_degrees=90)`. Pass
`timeout=` to bound how long a call may run (`turn_in_place(angle_degrees=-90,
timeout=20)`).

**Read a sub-skill's output from `.data`.** Skills that return structured results expose
them on the result object. Here `turn = turn_in_place(...)` and then
`turn.data.turned_degrees` reports how far the robot actually turned.

**Handle failures with `SkillFailed`.** A sub-skill that fails raises `SkillFailed`
rather than returning an error tuple. Wrap fallible calls in `try`/`except` to recover —
the demo shrugs off a missed pick and keeps going:

```python theme={null}
try:
    pick_socks(timeout=60)
except SkillFailed:
    head_emotion(emotion="disappointed")
    self.say("No socks today.")
```

If you *don't* catch it, the exception propagates and your composing skill fails too —
which is often exactly what you want for a step that must succeed.

**Everything else is a normal skill.** The composing skill is still an ordinary
[code-defined skill](/software/skills/code-defined-skills): it declares
[`RobotState`](/software/skills/code-defined-skills/robot-state) dependencies
(`battery`), persists data across runs with `self.storage`, speaks with `self.say(...,
wait=True)`, and returns a `(message, SkillResult)` tuple.

<Tip>
  Because each sub-skill call is its own step, a composed routine is easy to follow in the
  app and easy to interrupt — the agent can cancel the routine between steps, or right in
  the middle of a long-running sub-skill.
</Tip>

## When to compose vs. write from scratch

| Reach for composition when…                           | Write a flat skill when…                                                                                                         |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| The building blocks already exist as skills           | You need low-level [interface](/software/skills/code-defined-skills/body-control-interfaces) control the sub-skills don't expose |
| You want each step visible and separately cancellable | The steps are tightly coupled and shouldn't be interrupted mid-sequence                                                          |
| You're mixing scripted skills and learned policies    | Everything is a few interface calls with no reusable sub-behavior                                                                |

Composing skills is also how you turn a one-off demo into a reusable capability: name the
routine, give it `guidelines()`, and the agent can trigger the whole chain with a single
skill call.
