← Files KoraARCHIVED FILE

skills/kora-workflow-builder/references/workflow-flow-nodes.md

7.28 KB · Oct 4, 2026 · 12:29 UTC

↓ Download file

# 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