Skip to main content

YAML Flow Authoring

Flows define processing pipelines as YAML files. The steps-based format is the human-authored representation that compiles to an internal graph (nodes + edges) for execution.

Steps format

A flow is a list of sequential steps. Each step references a tool by name and optionally provides configuration:

steps:
- tool: pseudo-translate
config:
targetLocale: fr
expansionPercent: 30
prefix: "["
suffix: "]"

Source and sink

A flow carries only its steps. Where content enters and leaves are bindings resolved when the flow runs — a file, the project store, a .klz workspace, an interchange import/export, or none — not fields of the flow document. A flow declares a binding only when it is intrinsic to the flow (e.g. a check flow that produces no document sets sink: none), and never a path:

sink: none # only when intrinsic; otherwise omit and let the run decide
steps:
- tool: qa-check

At the CLI the binding comes from -i / -o (a plain path is detected, a scheme: is explicit), the project / .klz you are in, or auto-detection; kapi run <flow> --explain shows the resolved source → sink. See AD-026: Flow I/O Binding.

Step labels

Add a label for readability in the UI graph view:

steps:
- tool: pseudo-translate
label: Generate test translations
config:
targetLocale: fr

Sequential steps

Steps execute in order. The output channel of one tool feeds into the input channel of the next:

steps:
- tool: create-target
config:
targetLocale: fr
copySource: true

- tool: search-replace
config:
pairs:
- search: "TODO"
replace: ""
target: true

- tool: qa-check
config:
targetLocale: fr

This creates a three-step pipeline: create the target, clean up placeholder text, then run quality checks.

Parallel blocks for fan-out

Use parallel: to run multiple tools concurrently on the same stream of Parts. Each branch receives a copy of the input and produces independent output:

steps:
- tool: create-target
config:
targetLocale: fr
copySource: true

- parallel:
- tool: word-count
label: Count words
config:
targetLocale: fr
- tool: qa-check
label: Quality checks
config:
targetLocale: fr
- tool: chars-listing
label: Character inventory
config:
targetLocale: fr

All three analysis tools run at the same time, each in its own goroutine.

Transformers

Tools that rewrite the source — redaction, whitespace/markup normalization, a source-mutating script — are ordinary steps in the same ordered list as everything else. A transformer returns an edit plan; the framework applier performs the rewrite inline and in order, rebasing surviving run-anchored overlays (segmentation, terms) onto the new runs, so each transformer settles the source before later steps observe it.

steps:
- tool: redact
config:
detectors: [rules]
rulesPath: redaction-rules.yaml

- tool: ai-translate
config:
targetLocale: fr

A placement pass (core/flow/placement.go) validates transformer positions beside data-flow validation at every flow build and load:

  • Error — a transformer follows a step that produces a committed target (rewriting source orphans the targets). A transformer that produces targets itself, such as unredact, is exempt.
  • Error — a recoverable transformer (redact) follows a step with the remote-source-egress side effect, except a step producing an input the transformer's config-resolved contract requires (a cloud NER step feeding entity-driven redaction is the documented detection trade-off, AD-020). AI tools configured with a local provider (ollama, demo) carry no egress effect.
  • Warning — a transformer placed later than its earliest valid slot, since every overlay present at apply time must be rebased.

A flow that declares the removed source_transforms: field is rejected at load with a migration error directing you to list the transformers as ordered steps. See the tool-system AD for the immutability model and the producer/applier split.

How steps compile to the graph

The StepsToGraph() function transforms a StepsSpec into FlowNode and FlowEdge slices:

  1. Each sequential step becomes a tool node, chained by edges
  2. A parallel: block creates multiple tool nodes, all connected from the previous node (fan-out)
  3. After a parallel block, subsequent steps connect from all branch endpoints (fan-in)

The graph is tool nodes only. The flow's source and sink are bindings supplied at run time (AD-026), not nodes in the graph.

The resulting graph is what the Executor runs -- each node becomes a goroutine connected by buffered channels.

Example flows

Translation pipeline

A typical translation flow with TM leverage, AI translation for new blocks, and quality checks:

steps:
- tool: create-target
config:
targetLocale: fr
copySource: false

- tool: tm-leverage
label: Apply TM matches
config:
targetLocale: fr
fuzzyThreshold: 75

- tool: ai-translate
label: Translate remaining
config:
targetLocale: fr
provider: anthropic

- tool: qa-check
label: Quality checks
config:
targetLocale: fr

Fan-out analysis

Run multiple analysis tools in parallel after pseudo-translation:

steps:
- tool: pseudo-translate
config:
targetLocale: qps-ploc
expansionPercent: 30

- parallel:
- tool: word-count
config:
targetLocale: qps-ploc
- tool: length-check
config:
targetLocale: qps-ploc
maxChars: 200
- tool: qa-check
config:
targetLocale: qps-ploc

Script filtering

Use the JavaScript script step to filter or transform parts programmatically:

steps:
- tool: script
label: Skip short blocks
config:
code: |
if (part.type === 'block') {
var text = part.block.source[0].content.text;
if (text.length < 3) {
skip();
}
}

- tool: pseudo-translate
config:
targetLocale: fr

Running flows

From the CLI

# Run a built-in composed flow
kapi run ai-translate-qa -i input.xliff --target-lang fr

# Run a flow defined in a .kapi project file
kapi run my-flow -p myproject.kapi -i input.json

# List available flows
kapi flows

Programmatically

spec := &flow.StepsSpec{
Input: "json",
Steps: []flow.FlowStep{
{Tool: "pseudo-translate", Config: map[string]any{
"targetLocale": "fr",
"expansionPercent": 30,
}},
{Tool: "qa-check", Config: map[string]any{
"targetLocale": "fr",
}},
},
}

nodes, edges, err := flow.StepsToGraph(spec)
// Build and execute with Executor...