Skip to content

Skill composition

Composing skills into workflows - call chains, shared outputs, error propagation, and design patterns.

Last updated: 2026-04-30

Skill composition

Skills are designed to compose. A complex workflow is a sequence of simple, well-tested skills wired together. This page covers the patterns for doing that reliably.

The call chain

The most direct composition pattern: skill A calls skill B, which calls skill C.

# skill: weekly-nav-summary
steps:
  - call: get-nav-data        # calls another skill
    args:
      fund: "{{ inputs.fund_id }}"
      date: "{{ inputs.report_date }}"
    capture: nav_data

  - call: format-nav-report   # uses the output of the first call
    args:
      data: "{{ nav_data }}"
      template: "weekly-summary"
    capture: report_doc

  - call: distribute-report   # sends the formatted output
    args:
      document: "{{ report_doc }}"
      recipients: "{{ inputs.recipient_list }}"

Each step in the chain uses the capture key to name its output. Subsequent steps reference captured outputs by name.

Parallel execution

When two skills do not depend on each other’s outputs, they can run in parallel:

steps:
  - parallel:
    - call: check-feed-freshness
      args:
        feed_name: "equity-prices"
      capture: equity_freshness
    - call: check-feed-freshness
      args:
        feed_name: "fixed-income-prices"
      capture: fi_freshness

  - condition: "{{ !equity_freshness.is_fresh || !fi_freshness.is_fresh }}"
  - call: send-data-ops-alert
    args:
      stale_feeds: "{{ [equity_freshness, fi_freshness] | selectattr('is_fresh', 'eq', false) }}"
  - end_condition

Parallel steps complete before the next sequential step begins. If any parallel step fails and is not marked optional, the skill fails.

Error propagation

By default, any step failure propagates up and fails the calling skill. You can override this with on_error:

- call: get-external-price-feed
  args:
    ticker: "{{ inputs.ticker }}"
  capture: external_price
  on_error:
    strategy: fallback
    fallback_value:
      price: null
      source: "unavailable"
    capture: external_price   # overwrite with fallback

Options for on_error.strategy:

StrategyBehavior
failDefault. Propagates the error up.
fallbackContinues with a defined fallback value.
skipSkips this step and continues. Captured variable is unset.
retryRetries the step up to max_retries times before failing.

Skill design principles

One responsibility per skill

A skill should do one thing. “Get NAV data” is a skill. “Get NAV data and format it and send it” is three skills.

Why it matters: single-responsibility skills are testable in isolation, reusable in other workflows, and easier to debug when something goes wrong.

Never hardcode environment values

Anything that differs between sandbox and production - URLs, API keys, threshold values - belongs in vault.json or a knowledge document. Skills reference these via context variables:

# Good
- action: plexifact.catalog.connect
  args:
    catalog: "{{ vault.data.catalog }}"

# Bad
- action: plexifact.catalog.connect
  args:
    catalog: "plexifact://prod/catalog"

Document inputs and outputs

Every field in inputs and outputs should have a description. This documentation is what makes a skill usable by someone who did not write it - which includes you, six months from now.

Version intentionally

Increment the skill version when inputs or outputs change in a way that breaks existing callers. Agents reference skills by name; if an agent was written against version 1.0 and the output schema changes in 2.0, the agent needs updating.

Use the deprecated flag to signal that a skill is being replaced, giving callers time to migrate before the old version is removed.

Common fund-operations composition patterns

Pattern 1 - Monitor and alert

check-condition → if stale/anomalous → format-alert → send-notification → audit-log

Pattern 2 - Report generation

fetch-data → validate-data → transform-data → format-report → distribute → audit-log

Pattern 3 - Reconciliation

fetch-source-a → fetch-source-b → compare-values → if-mismatch → create-exception-ticket → audit-log

All three patterns share the same tail: audit-log. Every agent output, whether an alert, a report, or a reconciliation exception, should end with an audit entry. Operational processes require audit trails.

Was this page helpful?

Edit on GitHub