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

# Ordered Blocks

> Strict sequence control for when steps must execute in a fixed order.

Most skills don't need this. The previous levers handle the majority of
enterprise requirements without strict sequencing. Reach for an ordered block
only when the *order itself* is the requirement: regulatory processes,
multi-step approvals, compliance sequences.

## When to use an ordered block vs. another lever

| "I need..."                                       | Use this                                           |
| ------------------------------------------------- | -------------------------------------------------- |
| The LLM can't call tool X until value Y exists    | `tool_constraints` with `requires:`                |
| Only one branch of instructions visible at a time | `if:` markers                                      |
| Exact wording delivered verbatim                  | `utter:` triggers in frontmatter + `responses.yml` |
| Steps must execute in order, no skipping          | **Ordered blocks**                                 |

## The fence

```
:::ordered_block id=pick_card
steps:
  - id: ...
:::
```

The id goes on the fence line as an attribute, `id=pick_card`. The body between
the fences is YAML: the `steps:` list.

The prose around the block is what tells the LLM when to enter it, so say so in
the same sentence that references `@block.<block_id>`. When the block completes,
execution returns to the surrounding prose.

## Hybrid: one ordered block inline

The Card Replace skill has a problem: the agent sometimes checks eligibility
after the customer picks a card, or skips the check entirely. Order matters, and
prose isn't enforcing it. That section becomes a block; the rest stays prose.

```markdown skills/card_replace/skill.md theme={null}
---
name: Card Replace
description: Replace a credit card -- lost, stolen, damaged, or not received

tool_constraints:
  - process_card_replacement:
      requires: "session.card_replace.order_confirmed == True"
---

## Identify the reason

Ask why they need a replacement: lost, stolen, damaged, or not received. Once
the reason is recorded, invoke @block.pick_card to check eligibility and settle
which card this applies to.

:::ordered_block id=pick_card
steps:
  - id: check_eligibility
    execute_tool: check_account_replacement_eligibility

  - id: select_card
    instructions: |
      Show the customer the cards on their account and ask which one needs
      replacing. Accept a card name, label, or the last four digits.
    complete_when: "session.project.selected_card_id"
:::

## Handle the reason

if: session.card_replace.replacement_reason == 'damaged'
Ask who needs a replacement: just the customer's card, an authorized user's
card, or both.

if: session.card_replace.replacement_reason == 'stolen'
Tell the customer the card will be locked for their protection. Confirm, then
lock the card.

## Wallet and shipping

After the reason is handled, check digital-wallet eligibility. Then ask their
shipping preference, confirm the order, and process the replacement.
```

The block guarantees eligibility is checked before a card is chosen. The
`select_card` step is still a conversation: the LLM owns the wording and the
back-and-forth, and the step ends once `selected_card_id` is set. Everything
outside the block stays prose with `if:` scoping.

For the full step vocabulary, see
[Ordered block steps](/docs/maestro/reference/ordered-block-steps).

## Fully controlled

For the most regulated cases, the whole body is one block and there is no prose:

```markdown skills/card_replace/skill.md theme={null}
---
name: Card Replace
description: Replace a credit card -- lost, stolen, damaged, or not received
---

:::ordered_block id=main
steps:
  - id: check_eligibility
    execute_tool: card_replace_eligibility

  - id: collect_reason
    collect: replacement_reason
    utterance: utter_ask_reason_for_replacement

  - id: route_by_reason
    noop: true
    next:
      - if: "session.card_replace.replacement_reason == 'damaged'"
        then: damaged_path
      - if: "session.card_replace.replacement_reason == 'stolen'"
        then: stolen_path
      - else: fraud_review_path

  - id: damaged_path
    instructions: Ask who needs a replacement.
    complete_when: "session.card_replace.damage_processed"
    next: check_wallet

  - id: stolen_path
    execute_tool: lock_card
    next: check_wallet

  - id: fraud_review_path
    instructions: Review recent transactions, then confirm and lock the card.
    complete_when: "session.card_replace.card_locked_this_flow"
    next: check_wallet

  - id: check_wallet
    execute_tool: check_wallet_eligibility

  - id: ask_shipping
    collect: shipping_type
    utterance: utter_ask_shipping

  - id: confirm_order
    collect: order_confirmed
    utterance: utter_confirm_order
    next:
      - if: "session.card_replace.order_confirmed == True"
        then: process
      - else: END

  - id: process
    execute_tool: process_card_replacement

  - id: END
:::
```

### The tradeoff

Every step you put inside a block is a step the agent can no longer reorder. A
block is a state machine, and it holds that order even when the conversation
would naturally take another route: a customer who volunteers the answer to step
four while you are on step one, or who asks a question mid-sequence, is moving
against the grain of the block. The more of a skill you enclose, the more of
that flexibility you trade away, and the more scripted the conversation feels.

That trade is worth making where the order carries a regulatory or correctness
requirement, and rarely worth making anywhere else. In practice that is two to
five steps, not a whole skill, which is why the hybrid form covers most
enterprise needs.

## Boundary rules

1. **Local order, not global lock-in.** The user can trigger a different skill
   mid-block. The block pauses and, if the user comes back to it, continues at
   the same step.
2. **Prose and blocks coexist.** Prose enters a block via `@block.<block_id>`;
   when it completes, execution returns to the prose.
3. **`tool_constraints` are always enforced.** Whether the LLM or an
   `execute_tool:` step attempts the call, `requires:` is checked.
