Execution
Planning comes first
Section titled “Planning comes first”Nothing runs until the whole definition has been read and checked. The engine resolves every task name against the registry, builds an instance of each, and validates its parameters — then produces the execution plan.
The consequence is worth relying on: a mistake at the end fails the run at the start. A misspelled task name or a missing required parameter is caught before the first byte of work, not after twenty minutes of build time.
You can stop there deliberately:
kis flow -f build.yaml -d-d plans and validates without executing. It is the fastest check that a definition is sound, and
it costs a few milliseconds.
Workers
Section titled “Workers”A worker executes one node at a time. -w sets how many run concurrently:
kis flow -f build.yaml -w 4The default is one, which is correct for a sequential flow and wrong for a parallel one. A
map: node is bounded by the worker count as well as by its own max_concurrency — with one
worker, max_concurrency: 4 still runs one at a time. Set -w to at least the highest
concurrency in the flow.
More workers than that buys nothing. Nodes still run in the order the graph allows, and the extra workers sit idle.
When a task fails
Section titled “When a task fails”A failing node stops its own path. Where the run goes next depends on what you declared:
- name: deploy shell: ./deploy.sh retry: max_attempts: 3 backoff_base: 1s next: go: smoke-test error: go: rollbackThe order is: retry, then route.
- If
retry:is set, the node is retried up tomax_attemptswith backoff. - Once attempts are exhausted,
error:is followed if present. - With no
error:route, the failure propagates and the run stops.
An error route does not make the run successful
Section titled “An error route does not make the run successful”This surprises people, so it is worth stating on its own. Taking the error: branch handles the
failure; it does not erase it:
recoveredexecution engine instance Status: errored Errored Nodes: [deploy]The recovery ran, and the run is still reported as errored with deploy named as the node that
failed. That is deliberate — the record says what happened, not what you would have preferred. If
you want a handled failure to count as success, end that branch at a succeed: node.
Continuing past a failure
Section titled “Continuing past a failure”continueonerror: decides whether a failed node stops everything. It defaults to true for graph
flows and false for flat list: flows.
Reach for it sparingly. A flow that continues past failures produces runs that are green with holes in them, and the holes are found later by someone else.
Rerunning part of a flow
Section titled “Rerunning part of a flow”Two flags, for two different jobs:
kis flow -f build.yaml -s compile # start at 'compile', then continue normallykis flow -f build.yaml -t compile,test # run only 'compile' and 'test'-s skips the earlier nodes and runs everything from there. -t runs exactly what you name and
nothing else — the fast way to exercise one node while writing it.
Both bypass the nodes that would have set up their inputs, so anything a skipped node would have
put in a variable needs supplying with -v.
Affinity
Section titled “Affinity”By default the engine is free to place each node on any available worker. That is what you want, right up until a node depends on something an earlier node left on disk.
name: stateful-buildlist: trueaffinity: scope: single-agenttasks: - name: checkout shell: git clone {{repo}} /tmp/work - name: build shell: cd /tmp/work && makeWithout affinity, build may land on a worker that has never seen /tmp/work.
| Scope | Binds | Use for |
|---|---|---|
single-agent | Every node of one run | Local state built up during the run |
definition | Every run of this flow | A warm cache or checkout kept between runs |
payload | Runs whose named fields match | Per-tenant or per-region locality |
kis flow -f build.yaml --pin-agentkis flow -f build.yaml --affinity definitionkis flow -f build.yaml --affinity payload --affinity-key tenant_id,region--pin-agent is shorthand for --affinity single-agent.
Affinity is a constraint on scheduling, so it costs parallelism. Use it where local state genuinely requires it, not as a default.
Losing a worker
Section titled “Losing a worker”If the worker running a pinned flow disappears, the engine’s default is to resume: it clears the dead binding, places the remaining nodes on a healthy worker, and carries on from the last completed node.
That is the right default when the work is expensive and idempotent. It is the wrong one when the lost worker held local state the remaining nodes need — a half-written checkout, a running container. In that case restart instead:
affinity: scope: single-agent restart: truekis flow -f build.yaml --pin-agent --restart-on-failureThe trade is straightforward: resume is faster, restart is safer when nodes are not independent. Choose per flow, based on whether re-running a completed node would do any harm.
Reading a run
Section titled “Reading a run”Each run reports the nodes it completed:
execution engine instance Status: completed Completed Nodes: [setup route build-one(3) fan-out]build-one(3) means that node ran three times — a map: or foreach: over three items.
Control-flow nodes that never reach a worker (assign:, choice:, succeed:) do not appear in
the list; they leave no run record because there was no work to record.
To keep the log:
kis flow -f build.yaml --logfile build.logkis flow -f build.yaml -l debugSee also
Section titled “See also”- Node types — retries, error routes, and the nodes that suspend
- The flow file — variables, tables, multi-flow files
- Flow engines — which engine runs what