> ## Documentation Index
> Fetch the complete documentation index at: https://innateinc-theo-docs-skills-authoring-api.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Composing Skills

<Note>
  Requires **OS 0.7.0** or newer.
</Note>

A skill can call other skills. Declare the one you want as a type annotation, then call
it like a method — the runtime runs it to completion and returns control to your code.
This is how you build a high-level behavior out of capabilities you already have,
without re-implementing navigation, manipulation, or speech.

## Declare, then call

Sub-skills are declared exactly like [robot state](/software/skills/code-defined-skills/robot-state) — annotate the attribute with the skill's class:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
from innate import Skill, SkillReturn
from innate_skills.move_straight import MoveStraight
from innate_skills.turn_in_place import TurnInPlace

class PatrolCorner(Skill):
    move: MoveStraight
    turn: TurnInPlace

    def guidelines(self) -> str:
        return "Use to walk the robot around one corner of the room."

    def execute(self) -> SkillReturn:
        self.move(distance=1.0)
        self.turn(angle_degrees=90)
        return "Rounded the corner"
```

Each call:

* **Blocks** until the sub-skill finishes — no callbacks or polling.
* **Raises `SkillFailed`** if the sub-skill fails, so a failing step stops the routine unless you catch it.
* **Returns a `SkillOutput`** — `.message`, `.data`, and `.ok`.
* **Shows up as its own step** in the app timeline, so a composed routine is legible while it runs.

Declaring a sub-skill also means a missing or broken dependency fails the run **up front**, before the robot moves, instead of halfway through.

## Learned policies call the same way

A physical skill — a trained policy or recorded demonstration — has no class of its own,
so the catalog generates a typed reference for it inside its own folder. Import and
declare it exactly like a code skill:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
from innate_skills.pick_socks import PickSocks   # a trained ACT policy
from innate_skills.wave import Wave              # a recorded demonstration

class TidyUp(Skill):
    pick: PickSocks
    wave: Wave

    def guidelines(self) -> str:
        return "Use to greet the room and then pick up any socks on the floor."

    def execute(self) -> SkillReturn:
        self.wave()
        self.pick(timeout=60)     # bound how long a policy may run
        return "Tidied up"
```

The caller doesn't need to know how a sub-skill is implemented. See [Policy-Defined Skills](/software/skills/policy-defined-skills) for how these get trained.

## Reading a sub-skill's output

Skills that return structured data expose it on `.data`:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
result = self.turn(angle_degrees=90)
self.say(f"I turned {result.data.turned_degrees:.0f} degrees.")
```

## Handling failure

A sub-skill that fails raises `SkillFailed`. Catch it to recover, or let it propagate to
fail your skill too — which is often what you want for a step that must succeed:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
from innate import SkillFailed

try:
    self.pick(timeout=60)
except SkillFailed:
    self.say("No socks today.")
```

<Warning>
  Never catch bare `Exception` around a sub-skill call and swallow it. Cancellation travels
  as `SkillCancelled`, which must reach the runtime for a Stop to work. Catch `SkillFailed`
  specifically.
</Warning>

## A worked example

Talk, emote, shuffle, turn, and attempt a learned pick — six skills chained into one:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
from innate import Battery, Skill, SkillFailed, SkillReturn
from innate_skills.arm.arm_zero_position import ArmZeroPosition
from innate_skills.head_emotion import HeadEmotion
from innate_skills.move_straight import MoveStraight
from innate_skills.pick_socks import PickSocks
from innate_skills.turn_in_place import TurnInPlace


class RunDemo(Skill):
    arm_zero: ArmZeroPosition
    emote: HeadEmotion
    move: MoveStraight
    turn: TurnInPlace
    pick: PickSocks

    battery: Battery | None   # nice to have — the demo runs without a reading

    def guidelines(self) -> str:
        return "Run the demo routine. Use when the user asks for the demo."

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

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

        for distance in (0.2, -0.2):
            self.move(distance=distance)

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

        try:
            self.pick(timeout=60)
        except SkillFailed:
            self.emote(emotion="disappointed")
            self.say("No socks today.")

        if self.battery:
            self.say(f"Battery at {self.battery.percentage:.0%}.")
        return "Demo complete"
```

Everything else is an ordinary [code-defined skill](/software/skills/code-defined-skills): it declares robot state with annotations, persists counters with `self.storage`, speaks with `self.say(..., wait=True)`, and returns a plain success message.

<Tip>
  Because each sub-skill call is its own step, a composed routine is easy to follow in the app and easy to interrupt — a Stop unwinds the whole chain, wherever it is.
</Tip>

## Choosing a skill at run time

When you don't know which skill to call until the routine is running, dispatch by id:

```innatepy theme={"languages":{"custom":["/languages/python-typed.json"]}}
output = self.skills.run("innate-os/turn_in_place", angle_degrees=90, timeout=20)
if not output.ok:
    self.fail(output.message)
```

`self.skills.run()` **returns** a failed `SkillOutput` rather than raising, so you check `.ok` yourself. Prefer a declaration whenever the skill is known ahead of time — it's typed, your editor catches renames, and the dependency is verified before the run starts.

## 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 is also how a one-off demo becomes a reusable capability: name the routine, give it `guidelines()`, and the agent can trigger the whole chain with a single skill call.
