← Files KoraARCHIVED FILE
skills/kora-workflow-builder/references/workflow-flow-nodes.md
7.28 KB · Oct 3, 2026 · 06:30 UTC
# Workflow Flow Nodes
Workflows are BPMN-inspired YAML definitions. State is one top-level object.
Each node output shallow-merges into state.
## Starts
```yaml
start:
- type: message
name: external-event
input: EventInput
goto: first-node
- type: timer
schedule: "0 8 * * *"
goto: first-node
- type: manual
internal: true
input: StartInput
goto: first-node
```
Use `message` for top-level messages and `timer` for scheduled top-level
workflows. If an external provider event should start a workflow, add a
separate `triggers/*.yaml` resource that targets the message start name. A
workflow called by another workflow should expose a manual start with
`internal: true`. Bare top-level manual starts are invalid.
When a message, timer, or manual start must accept only an empty payload,
declare `input` with an object type that sets `additionalProperties: false` and
has no properties. Keep that closed input contract on the start so CLI, API,
and message-ingress callers cannot inject arbitrary initial workflow state. The
web start dialog omits controls for this schema and submits `{}`. Omitting
`additionalProperties` creates an open object whose arbitrary keys remain valid
workflow input.
Timer schedules fire with `{}` as their start input. If required values must
arrive with the start, use a message start; deployment rejects a timer schedule
whose declared input type requires values.
## Service
Use service nodes for custom code and external behavior.
```yaml
- id: read-prs
type: service
operation: read-github-prs
input: PullRequestQuery
output: PullRequestBatch
next: review
```
The `operation` resource runs a script. That script can call installed
extension functions through `@kora/runtime-sdk`.
Service nodes do not require `role`, `capability`, or assignment resources.
Add org-model resources only for human/agent task nodes.
## Human Or Agent Task
```yaml
- id: review
type: task
role: reviewer
capability: triage-pr
input: PullRequestBatch
output: TriageResult
promptAttachments:
imageArtifacts:
fields: [sourceImage]
textArtifacts:
fields: [extractedText]
maxCharsPerArtifact: 20000
retry:
maxAttempts: 2
retryOn: [agent_provider_error]
nonRetryableErrors: [agent_needs_human]
next: done
```
The role assignment decides whether a person or an agent performs the task.
When it resolves to a person, `output` is required, must refer to an object
type, and is the sole contract for the review fields and submitted value. For
`task.each`, `itemOutput` serves the same purpose and must also be an object
type.
Task retry is whole-node retry. Tasks default to one attempt; use `retry` only
for failures that are safe to run again. Agent structured-output repair remains
configured on `agentConfig.output.maxRepairAttempts` and does not retry the
whole task node.
### Supervised agent output
Use one explicit review task. The producer capability sets
`agentConfig.requiresOutputReview: true`, and every ordinary producer task sets
both its human/default successor and its agent-only review successor. The
producer object output type must set top-level `additionalProperties: false`:
```yaml
- id: prepare-assessment
type: task
role: analyst
capability: assess-case
input: Case
output: Assessment
agentOutputReview: review-assessment
next: apply-assessment
- id: review-assessment
type: task
role: supervisor
capability: review-assessment
input: Assessment
output: ReviewDecision
boundary:
- type: timer
duration: PT8H
interrupting: true
goto: escalate-review
next: route-review
```
| Performer and policy | Success path |
| --- | --- |
| human performer | `next` |
| agent, review not required | `next` |
| agent, review required | `agentOutputReview` only |
The review target must be an ordinary human-assigned task in the same execution
scope. Its declared input must admit the producer's complete structured output.
The review task returns an ordinary typed decision; gateways and explicit loops
model approval, rejection, requested changes, application, and escalation.
Use ordinary `next` to the review task when review applies to human-produced
work too. Use an explicit proposal -> human decision -> service/action sequence
when approval must happen before an irreversible side effect. Output review
never authorizes tools or side effects.
Agent errors and `agent_needs_human` use error boundaries, not the successful
review edge. Put human-response timers on the review task; they start when that
task activates and can route reminders or escalation. A producer timer is a
separate clock.
`promptAttachments` applies only to agent tasks. The `fields` entries are
top-level `x-kora-type: file` fields from the task input type, not artifact IDs
or JSON pointers. Use it for selected image artifacts or extractor-produced text
artifacts that should be included in the initial remote model prompt.
## Collections
Use `task.each` or `call.each` for runtime collections.
```yaml
- id: review-each
type: task.each
role: reviewer
capability: triage-pr
collection: $.pullRequests
itemInput: PullRequest
itemOutput: TriageResult
promptAttachments:
textArtifacts:
fields: [extractedText]
retry:
maxAttempts: 2
retryOn: [agent_provider_error]
inputMapping:
pullRequest:
from: $.item
outputMapping:
reviews:
from: $.results
next: done
```
For `task.each`, prompt attachment fields refer to top-level fields on
`itemInput` after `inputMapping`.
Do not use a review-required capability on `task.each`. Use `call.each` to a
callable child workflow containing one producer task and its explicit review
task, so each item has its own review, timer, evidence, and result.
## Decisions And Gateways
Use `decision` for reusable decision logic and gateways for inline routing.
```yaml
- id: choose-path
type: gateway.exclusive
paths:
- condition: approved == true
goto: done
- default: true
goto: needs-work
```
## Receive And Timers
`receive` waits for a named message. `timer` waits for a duration.
```yaml
- id: wait-for-webhook
type: receive
catch: external-update
output: ExternalUpdate
next: done
```
External provider events should enter through Platform-owned trigger ingress,
or extension callbacks when the integration is extension-owned. A trigger
starts a workflow through exactly one matching message start; it does not
resume a `receive` node.
## Run Evidence
Every run records node inputs and outputs as execution events. By default the
recorded values are truncated at deployment caps (long strings, large arrays
and objects, deep nesting), and truncation is marked in place in the recorded
value. Sensitive-looking keys are always redacted.
A workflow opts out of truncation with one optional top-level field:
```yaml
apiVersion: kora/v1
kind: Process
metadata:
name: quality-deviation-review
evidence: full # optional; default standard (deployment caps)
```
`evidence: full` applies to all nodes of the workflow and is resolved once at
run start; redaction still applies. Declare it on workflows whose runs will be
exported (`kora run export`) or pinned (`kora run save`) as test cases, so the
captured evidence is complete rather than truncated. The recorded mode is
stamped on the run's `run_started` event and echoed in the export's
`run.json`.
SHA-256: 86514a17303616d2b1c733197cca2674763348f2d57437ac639a784ebcac11dd