Skip to content

Skill Composition and Reuse

Why do we need composition?

If every skill is a focused capability, a real user goal will often require several skills to work together.

For example:

"Review this broken PR, fix the test and update the documentation."

This contains several responsibilities:

PR Review Skill
Test Failure Analysis Skill
Implementation Skill
Documentation Update Skill

The question is:

How do we combine them without creating a new mega-skill?

That is the skill-composition problem.

Atomic vs composite capability

An atomic skill has one well-bounded task-level responsibility.

For example:

Analyze Test Failure

A composite capability can use several skills:

Resolve Failing Pull Request
├── review_pull_request
├── analyze_test_failure
├── propose_code_change
└── verify_tests

The composite capability does not necessarily need to be another “skill”. It can be:

  • a workflow,
  • an orchestrator service,
  • an agentic loop,
  • a planner/executor system.

This is an important boundary.

When should one skill call another?

A direct skill-to-skill dependency can be valid when the dependency is:

  • stable,
  • one-directional,
  • exposed through a narrow contract,
  • a natural part of the parent capability.

For example:

Release Notes Skill
      ↓
Change Summary Skill

If the release-notes skill always works from a structured change summary, this can be a reasonable dependency.

But if the call graph is dynamic:

Skill A may call B or C
B may call D
D may call A

we are dealing with runtime orchestration rather than simple skill dependency.

Prefer orchestration over hidden nesting

Weak:

User
 ↓
Skill A
  ↓ hidden call
Skill B
  ↓ hidden call
Skill C

The caller cannot see:

  • what ran,
  • where it failed,
  • token/tool cost,
  • required permissions,
  • how retry should work.

Better:

Orchestrator
├── Skill A
├── Skill B
└── Skill C

where the execution path is explicit.

Composition should be observable and controllable rather than hidden inside prompt behavior.

Sequential composition

The simplest pattern:

Skill A output
      ↓
validation / mapping
      ↓
Skill B input
      ↓
Skill B output

Example:

PR Review Skill
      ↓
structured findings
      ↓
Fix Planning Skill

Typed contracts are especially valuable here.

Weak chain:

A: "There may be something wrong around retries..."
 ↓
B re-interprets free text

Better:

{
  "finding_type": "NON_IDEMPOTENT_RETRY",
  "severity": "HIGH",
  "location": "RetryService.java:87",
  "evidence": "charge() is retried without an idempotency key"
}

Parallel composition / fan-out

A task may be analyzed from independent perspectives:

PR diff
 ├── Security Review Skill
 ├── Architecture Review Skill
 └── Test Coverage Review Skill
        ↓
     Aggregator

This works when subtasks are genuinely independent.

Benefits:

  • parallelizable,
  • independently evaluable,
  • specialized skills.

Costs:

  • higher cost,
  • conflict reconciliation,
  • duplicate findings must be merged.

Aggregator pattern

An aggregator handles:

multiple structured results
        ↓
merge / deduplicate / prioritize
        ↓
final result

It does not necessarily need to be an LLM.

Severity ordering and deduplication by location+rule may be deterministic. Semantic conflicts may require a model or human review.

Shared primitive vs shared skill

Not every reusable element is a skill.

For example:

read_repository_file

is a tool/capability primitive.

summarize_code_change

may be a skill.

normalize_severity_enum

is probably a deterministic helper function.

Ask:

Does this require semantic reasoning, or is it a normal software primitive?

Do not turn every reusable function into an agent skill.

Dependency inversion in skill composition

A skill does not need to depend directly on a concrete skill implementation.

More coupled:

ReleaseNotesSkill requires ChangeSummarySkillV3

Better:

ReleaseNotesSkill requires capability:
change_summary_provider

The runtime can map:

change_summary_provider
       ↓
ChangeSummarySkill v3

or in tests:

change_summary_provider
       ↓
Fixture implementation

This is ordinary dependency inversion.

State ownership in composition

With several skills, ask:

Who owns the shared state?

Usually prefer:

Orchestrator / Runtime owns workflow state
Skill receives explicit input
Skill returns explicit output

over:

Skill A silently writes shared memory
Skill B silently reads it

Explicit state flow is easier to debug.

WorkflowState
   ↓         ↑
Skill A   result A
   ↓
WorkflowState updated
   ↓
Skill B

Error propagation

If Skill A fails, Skill B should not automatically run.

For example:

Fetch Current Deployment Skill
        ↓ TOOL_UNAVAILABLE
Risk Analysis Skill

Risk analysis may be invalid without current deployment data.

Workflow policy can decide:

TOOL_UNAVAILABLE → stop
PARTIAL_RESULT → continue with warning
INSUFFICIENT_CONTEXT → request more input

Do not let an implicit nested chain decide this by accident.

Failure isolation

In parallel composition:

Security Review → SUCCESS
Architecture Review → SUCCESS
Test Review → TOOL_TIMEOUT

A valid overall outcome may be:

PARTIAL_RESULT

with a report such as:

Test coverage could not be evaluated because CI data was unavailable.

This is better than discarding all results because one optional branch failed.

Workflow vs Agent Loop

If the order is known in advance:

A → B → C → D

a workflow is natural.

If the next capability depends on the previous observation:

observe
 ↓
decide next skill
 ↓
execute
 ↓
observe

we are moving toward an agentic loop.

Workflow example:

extract invoice data
 → validate
 → store

Agentic example:

investigate production incident
 → choose which signal to query next based on observations

Do not use an agent loop where a simple workflow is enough.

Example: PR resolution pipeline

Pull Request
    ↓
PR Review Skill
    ↓
findings[]
    ↓
Fix Planning Skill
    ↓
change_plan[]
    ↓
Implementation Skill
    ↓
patch
    ↓
Test Runner tool
    ↓
Test Failure Analysis Skill if needed
    ↓
Verification

There are several boundaries here:

  • review is a semantic task,
  • fix planning is a semantic task,
  • implementation is a semantic task,
  • test running is a deterministic/tool task,
  • failure analysis is a semantic task.

Do not compress all of them into:

CodingSkill.doEverything()

Composition depth

Deep nesting can become problematic:

A → B → C → D → E → F

because it increases:

  • latency,
  • cost,
  • failure probability,
  • observability requirements,
  • context drift.

Use explicit execution budgets and regularly review whether the chain is necessary.

Circular dependencies

Avoid uncontrolled cycles such as:

Architecture Review Skill
       ↓
Refactoring Skill
       ↓
Architecture Review Skill

without an explicit loop controller.

Circular capability graphs can lead to unbounded execution.

If repetition is required, model it as an agentic loop or workflow retry policy with an explicit stop condition.

Skill-library modularity

A good skill library resembles a good modular codebase:

clear responsibilities
explicit interfaces
small dependency graph
few shared primitives
no hidden globals

The goal is not the maximum number of skills.

The goal is useful modularity.

Anti-pattern: mega-skill

SoftwareEngineeringSkill
├── requirements
├── architecture
├── implementation
├── review
├── test
├── deploy
└── incident response

This is:

  • hard to route,
  • permission-heavy,
  • hard to evaluate,
  • difficult to secure,
  • prone to giant prompts.

Anti-pattern: skill-as-function-call fetish

Not every 20-line deterministic helper should become a skill.

If the task is:

calculate retry delay

and the formula is known, write code.

Anti-pattern: hidden shared memory

Skill A writes "current_plan" to memory
Skill B assumes it exists

This creates temporal coupling.

Prefer explicit input/output contracts.

Anti-pattern: workflow logic inside a skill prompt

Weak:

"First call skill A, then B, unless C, then retry D..."

inside a giant system prompt.

Put deterministic control flow in orchestration code where possible.

Takeaways

  • Skill composition is about reusing focused capabilities, not building new mega-skills.
  • Sequential and parallel composition are both useful patterns.
  • Execution orchestration should be explicit and observable.
  • Typed contracts reduce semantic drift between skills.
  • Shared primitives are not necessarily skills; ordinary code and tools still matter.
  • Prefer capability dependencies over concrete implementation dependencies.
  • Shared state is typically owned by the runtime/workflow, not hidden skill memory.
  • Failure policy should belong to the orchestrator rather than implicit nesting.
  • Use workflows for known steps; use agentic loops for dynamic next-action decisions.
  • Replace circular dependencies with explicit loops and stop conditions.