concept · 08

Compose at scale

Write a building block once, compose it anywhere, and layer per-project rules instead of forking. Pick the cheapest sufficient model per step.

Write the building block once; compose it anywhere

A capability is a reusable building block — a small sub-workflow that does one thing (your create_pr, your verify-tests) behind a typed input/output contract. A larger workflow that wires several capabilities together is the orchestrator; it hands each one its inputs and collects its outputs. Fix a bug in create_pr once and every orchestrator that uses it inherits the fix. Break its contract and every dependent fails at load time, not in production.

workflows:
  cap.verify.workspace-green:        # a capability: one job, reusable
    snippet:                         # the snippet block IS its typed contract
      inputs:  { repo: { type: string } }
      outputs: { green: { type: boolean } }
    # ... its own state machine
  flow.add-feature:                  # an orchestrator: composes capabilities
    states:
      verifying:
        transitions:
          verify:
            target: done
            executor:
              kind: workflow
              definitionId: cap.verify.workspace-green
              use:
                inputs:  { repo: "$.context.repo" }
                outputs: { green: "$.context.green" }

Layer the rules; don’t fork the policy

The reason you’d fork create_pr in the first place is a per-repo rule — this project squash-merges, that one needs two reviewers. Instead of three copies, the rules layer: your personal defaults, your project’s rules, your team’s rules — stacked, so a project tweak overrides one rule without forking the whole block. The shared create_pr stays one definition; each layer only declares what it changes.

The cheapest sufficient model, per step

The model is chosen per step. A trivial classification can run on a small local model; a hard refactor can escalate to a frontier model; a deterministic step needs no model at all. You stop paying frontier prices for work that doesn’t need frontier reasoning. Name a model directly, or tag the step with an affinity: and let models.yaml resolve which model fills it — so swapping models is one file, not a find-and-replace across workflows.

triaging:
  transitions:
    triage:
      target: triaged
      executor:
        kind: llm
        model: anthropic:claude-sonnet-4-6   # cheapest sufficient for this step
        prompt_template: |
          Classify the issue in $.context.issue_body. Pick one transition.
        max_iterations: 3
← All concepts