Pipeline YAML Reference

transforms (UTL-X)

The transform stanza lives inside a connection entry and declares how the MPPM payload is transformed between the upstream component's output schema and the downstream component's input schema. UTL-X transforms are pure, stateless, and execute inside the receiving wrapper — no extra component node, no extra topic hop.

Three mapping modes

MODE 1
Plain connection
No transform stanza. Schemas match. The envelope payload flows through unchanged.
MODE 2
UTL-X on the arrow
Transform stanza present. Pure, stateless UTL-X executes in the receiving wrapper in-process. Two sub-variants: inline and ref.
MODE 3
Mapping component
No transform stanza — the mapping is an explicit component node. Required for external calls, aggregation, non-UTL-X engines, or MPPM step traceability.

Mode 1 — plain connection

No transform stanza in the connection. Schemas match or are compatible. The envelope is published as-is.

yaml — Mode 1
connections:
  - id:   conn-inbound-to-enricher
    from: { component: http-inbound,      port: output }
    to:   { component: customer-enricher, port: input  }
    route:
      topic:        logistics.orders.order-fulfillment.http-inbound.out
      subscription: logistics.orders.order-fulfillment.customer-enricher.sub
      schema_ref:   logistics.schemas.order-received:1.0.0
        # no transform stanza — plain, schemas match

Mode 2 — UTL-X inline

The UTL-X mapping body is embedded directly in the YAML. Best for small, pipeline-unique mappings under ~20 field assignments. The mapping accesses one or more steps from this pipe's MPPM envelope, declared in the UTL-X header as named inputs.

yaml — Mode 2 inline
transform:
  type:   utlx
  mode:   inline
  mapping: |
        %utlx 1.0
        input: current json, previous json   // two steps from this pipe only
        output json
        ---
        {
          orderId:      $current.id,
          customerId:   $current.customer.id,
          prevOrderRef: $previous.id
        }
  target_schema_ref: logistics.schemas.order-to-validate:1.0.0
💡

An inline mapping can reference multiple steps from the same pipe using a multi-input UTL-X header: input: current json, previous json. It cannot access payloads from other pipes — for that, use Mode 2 ref with N-input or Mode 3.

Mode 2 — UTL-X ref

References a mapping stored in the Mapping Registry by version. Use for complex mappings, mappings reused across pipelines, or mappings that need an independent test suite and version lifecycle.

yaml — Mode 2 ref
transform:
  type: utlx
  mode: ref
  ref:  logistics.mappings.enrich-to-validate:1.2.0
  target_schema_ref: logistics.schemas.order-to-validate:1.0.0

Mode 2 — N-input (same-chain fan-in)

Multiple arrows converge on a single UTL-X transform. One arrow is the trigger (owns the transform stanza, marked trigger: true). Other arrows are context sources referenced by alias. All inputs must share the same correlation_id (same-chain). The wrapper assembles the full context before executing the mapping.

yaml — Mode 2 N-input, trigger arrow
# Trigger arrow — owns the transform definition
connections:
  - id:   conn-order-to-dispatcher
    from: { component: inventory-validator, port: output }
    to:   { component: fulfillment-dispatcher, port: input }
    route:
      topic:             logistics.orders.order-fulfillment.inventory-validator.out
      subscription:      logistics.orders.order-fulfillment.fulfillment-dispatcher.order-sub
      source_schema_ref: logistics.schemas.order-validated:1.0.0
    transform:
      type:    utlx
      mode:    ref
      ref:     logistics.mappings.compose-dispatch:1.0.0
      trigger: true                       # this arrow fires execution
      inputs:
        - alias:             order
          arrow:             conn-order-to-dispatcher
          source_schema_ref: logistics.schemas.order-validated:1.0.0
          window_depth:      3
        - alias:             pricing
          arrow:             conn-pricing-to-dispatcher   # context arrow
          source_schema_ref: logistics.schemas.order-pricing:1.0.0
          window_depth:      2
      correlation_mode:  same_chain        # all inputs must share correlation_id
      target_schema_ref: logistics.schemas.fulfillment-dispatch:2.0.0

  # Context arrow — no transform stanza
  - id:   conn-pricing-to-dispatcher
    from: { component: pricing-calculator, port: output }
    to:   { component: fulfillment-dispatcher, port: input }
    route:
      topic:             logistics.orders.order-fulfillment.pricing-calculator.out
      subscription:      logistics.orders.order-fulfillment.fulfillment-dispatcher.pricing-sub
      source_schema_ref: logistics.schemas.order-pricing:1.0.0
        # no transform stanza — context arrow, trigger owns the mapping

Inside the UTL-X mapping script, each alias is accessible as a named input with a $ prefix. The wrapper passes the current payload and step window of each arrow under its alias:

utlx — N-input mapping body
%utlx 1.0
input: order json, pricing json
output json
---
{
  dispatchId:  $order.header.orderId,
  customerId:  $order.customer.id,
  totalAmount: $pricing.summary.grossTotal,
  currency:    $pricing.summary.currency,
  lines: $order.lines |> map(line => {
    sku:   line.productCode,
    qty:   line.quantity,
    price: $pricing.lines[line.lineId].unitPrice
  })
}

Mode 3 — full mapping component

No transform stanza on the connection. The mapping is an explicit spec.components entry with its own canvas node, log topic, error port, and scaling configuration. Required when:

transform stanza — field reference

FieldTypeReqDescription
typeenumrequiredAlways utlx for Mode 2 transforms.
modeenumrequiredinline — mapping body embedded in YAML. ref — mapping referenced from Mapping Registry.
mappingstring (|)inline onlyMulti-line UTL-X mapping body. Use YAML block scalar (|). Indented as a child of the connection stanza.
refstringref onlyMapping Registry reference in namespace.mappings.name:version format.
triggerbooleanoptionalSet to true on the arrow that fires execution in N-input mappings. Exactly one arrow in a fan-in group must be the trigger. Default false.
inputs[]arrayoptionalNamed input list for N-input mappings. Each entry declares an alias, an arrow reference, a source schema, and a window depth. See inputs[] fields below.
inputs[].aliasstringrequiredName used to access this input in the UTL-X script as $alias. Should be semantic — e.g. order, pricing, customerProfile.
inputs[].arrowstringrequiredConnection id of the arrow that carries this input. For the trigger arrow this is self-referential.
inputs[].source_schema_refstringoptionalSchema of the current payload arriving on this arrow. Used by the IDE mapping editor to render the input field tree.
inputs[].window_depthintegeroptionalHow many steps back from the MPPM envelope to make accessible for this alias. Defaults to the pipeline spec.defaults.step_depth.
correlation_modeenumoptionalsame_chain — the wrapper asserts all arriving envelopes share the same correlation_id before assembling context. Required for N-input Mode 2. Mismatched envelopes are routed to the error topic.
target_schema_refstringoptionalSchema the UTL-X mapping must produce. The wrapper validates the mapping output against this schema before passing to the downstream component.

Decision guide

SituationMode
Schemas match exactlyMode 1 — no stanza
Simple field rename/coercion, unique to this pipeline, <20 assignmentsMode 2 inline
Complex or reused mapping, needs independent versioningMode 2 ref
Multiple arrows, same correlation_id, pure compositionMode 2 ref N-input
External call, side effect, or cardinality changeMode 3 component
Independent correlation chains (different correlation_ids)Mode 3 stateful-join
Non-UTL-X engine (XSLT, JSONATA, JQ)Mode 3 component
Transform must appear in MPPM step window (compliance)Mode 3 component
ℹ️

Mode 2 transforms do not add an entry to the MPPM step window. The topic carries the untransformed payload — the transform is receiver-side and happens after dequeue. If compliance requires full step-level auditability of a specific transform, use Mode 3.