YAML Flow Authoring
A flow is a YAML file that lists the tools a run applies, in order. You write it as a list of steps, and kapi compiles the steps into a graph of tool nodes and 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:
expansionPercent: 30
prefix: "["
suffix: "]"
The run supplies the target locale to every step
(--target-lang at the CLI, or the project's declared targets under kapi run / kapi up), so one flow serves every locale; a targetLocale written
into a step's config is outranked by the run's language.
Source and sink
A flow carries only its steps. Where content enters and leaves is set by
bindings that the run resolves: a file, the project store, a .kpz
workspace, an interchange import/export, or none. 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 it and the run sets it
steps:
- tool: qa
At the CLI the binding comes from -i / -o (a plain path is detected, a
scheme: is explicit), the project / .kpz you are in, or auto-detection;
kapi run <flow> --explain shows the resolved source → sink. See
E-04: 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:
expansionPercent: 30
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:
copySource: true
- tool: search-replace
config:
pairs:
- search: "TODO"
replace: ""
target: true
- tool: qa
This creates a three-step pipeline: create the target, clean up placeholder text, then run quality checks.
Steps run in order
A flow runs its steps as one ordered chain: each tool reads the Parts the step before it emitted. To run several checks, list them one after another:
steps:
- tool: create-target
config:
copySource: true
- tool: qa
label: Quality checks
- tool: term-check
label: Terminology checks
- tool: xml-validation
label: XML validation
Each tool runs in its own goroutine, so the checks work on different Parts at
the same time. Within one tool, --parallel-blocks (or a recipe's
parallel_blocks) spreads blocks across workers for a tool built as a plain
tool.BaseTool with a block handler. The AI and MT tools keep their own
processing, described in
Parallel block processing.
The steps parser also accepts a parallel: block, but no runner executes one:
kapi run stops with an error before processing a flow that contains it.
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: translate
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 or process-exec 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, C-10). 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:
- Each sequential step becomes a tool node, chained by edges
- A
parallel:block creates multiple tool nodes, all connected from the previous node, and the step after it connects from every branch; this shape exists in the definition only and cannot run
The graph holds tool nodes only. The run supplies the flow's source and sink as bindings (E-04).
The host builds the ordered tool chain from the steps, and the Executor runs it: each tool becomes a goroutine connected to the next by a buffered channel.
Example flows
Translation pipeline
A typical translation flow with content-memory leverage, AI translation for new blocks, and quality checks:
steps:
- tool: create-target
config:
copySource: false
- tool: recycle
label: Apply memory matches
config:
fuzzyThreshold: 75
- tool: translate
label: Translate remaining
config:
provider: anthropic
- tool: qa
label: Quality checks
Run it for each locale you ship:
kapi run my-translate -i input.xliff --target-lang fr
kapi run my-translate -i input.xliff --target-lang de
Checks after pseudo-translation
Run analysis tools over pseudo-translated output:
steps:
- tool: pseudo-translate
config:
expansionPercent: 30
- tool: term-check
- tool: qa
config:
checkAbsoluteMaxCharLength: true
absoluteMaxCharLength: 200
kapi run pseudo-checks -i input.json --target-lang qps
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
Running flows
From the CLI
# Run a built-in composed flow
kapi run translate-qa -i input.xliff --target-lang fr
# Run a flow defined in a kapi.yaml project file
kapi run my-flow -p kapi.yaml -i input.json --target-lang fr
# List available flows
kapi flows
# Write no file; print the change set a pass of the project would apply
kapi up -p kapi.yaml --print-ops > change.json
kapi apply -p kapi.yaml change.json
A run that writes a file commits each document through the file home a
change set commits through: the destination is staged beside the file and
renamed onto it under the file's lock, only while the file still holds what it
held when the run began, so a file a person saved while the run worked keeps
their bytes. In a project each document the run wrote is recorded as the flow's
edit, with the source each translation was made from. --print-ops writes no
file and prints the operations the run would apply, addressed to the document
and the edition the run changed; kapi apply applies them to the same bytes.
Under materialize: on-converge a translation whose file does not exist yet
lives in the workspace home, so kapi apply keeps it there and kapi merge
writes its file.
Every file left out of the change set is named on stderr, among them a target
file the run writes whole from its source where kapi apply would write other
bytes, and a file the change service does not keep the edition in (an -o path
rather than the recipe's target). In a project, kapi run without -o writes
no file, so it prints nothing. See
E-09: The change contract.
Programmatically
An ad-hoc programmatic run has no --target-lang to inherit, so the locale is
passed in each step's config:
spec := &flow.StepsSpec{
Input: "json",
Steps: []flow.FlowStep{
{Tool: "pseudo-translate", Config: map[string]any{
"targetLocale": "fr",
"expansionPercent": 30,
}},
{Tool: "qa", Config: map[string]any{
"targetLocale": "fr",
}},
},
}
nodes, edges, err := flow.StepsToGraph(spec)
// Build and execute with Executor...
Related
- Tool Authoring with Parameter Schemas: building the tools a flow names.
- JavaScript Script Step: the JavaScript step used in the filtering example.
- E-04: Flows and I/O binding: how a run resolves a flow's source and sink.
- E-09: The change contract: how a run commits what it writes.