Skip to content

Run and author a workflow

Chat is improvisation: good for exploration, wrong for work you need to run the same way every time. For repeatable work keelson has workflows, plain YAML files that describe a DAG of nodes the engine executes deterministically. You will run one that already ships, then author one from scratch, break it on purpose to see what validation catches, and watch it run.

Your first run seeded eight starter workflows, the ones doctor counted, and you can run any of them before writing a line of YAML. List the catalog:

Terminal window
keelson workflow list
workflows:
- name: smoke-test
description:
Use when: User wants to verify the workflow engine end-to-end ...
nodeCount: 9
source: global
- ...

Run smoke-test first. It exercises every node type the engine supports on Bun alone, so a clean run is an end-to-end proof that the engine works on your machine. Run it with --watch:

Terminal window
keelson workflow run smoke-test --watch
◆ target: cwd=/Users/you
▶ run 4f2a1c9e (smoke-test)
· prompt-node …
· command-node …
· loop-node …
· bash-json-node …
· script-bun-node …
✓ bash-json-node
{"echoed":"no-input","timestamp":"2026-06-26T21:12:13.164Z"}
✓ script-bun-node
✓ prompt-node
✓ command-node
✓ loop-node
· downstream …
· gated …
downstream got: ok
gated-ok
✓ downstream
✓ gated
· merge …
merge-ok
✓ merge
· assert …
PASS: all node types verified
✓ assert
■ succeeded

Five nodes with no dependencies start together; the rest fall in behind them as their inputs land. The agent nodes did real turns (prompt-node was asked for exactly ok, and downstream read that answer back through the env channel), and the final assert node checks that every upstream node produced output and prints PASS. That is a nine-node workflow shipping in the box, exercising every node type with no authoring. Now write your own.

You just ran a workflow that ships in the box. Authoring your own is the same catalog, one directory over: a workflow is a YAML file in ~/.keelson/workflows, and the one you have in mind is not there yet. Run it and you hit the wall:

Terminal window
keelson workflow run hello-harness
error: no workflow named 'hello-harness' under /Users/you/.keelson/workflows

The exit code is 4, the CLI’s stable code for “not found”. The error names the directory it searched, and that directory is the whole catalog mechanism: a workflow is a YAML file in ~/.keelson/workflows. Ribs can contribute workflows there too, but nothing stops you from writing your own, so let’s fix this error the direct way.

Create ~/.keelson/workflows/hello-harness.yaml:

~/.keelson/workflows/hello-harness.yaml
name: hello-harness
description: Three node kinds, one dependency, one gate. A first workflow.
nodes:
- id: ask
prompt: "Reply with exactly the words 'engine answers' and nothing else."
idle_timeout: 30000
- id: check
bash: 'echo ''{"status":"ok"}'''
- id: report
bash: 'echo "the agent said: $KEELSON_NODE_ask_OUTPUT"'
depends_on: [ask]
- id: done
bash: "echo 'all checks passed'"
depends_on: [check, report]
when: "$check.output.status == 'ok'"

Four nodes, three ideas:

  • prompt runs an agent turn. The ask node sends its prompt to whatever provider the server registered, with an idle timeout so a stalled turn cannot hang the run.
  • bash runs a shell script. The check node emits JSON, and that matters downstream: JSON output unlocks dot-access like $check.output.status.
  • Edges are data, not order. report declares depends_on: [ask] and reads the upstream output through the KEELSON_NODE_ask_OUTPUT environment variable. done waits on two parents and adds a when: condition, so it runs only if the gate evaluates true.

The shape on the page is a diagram waiting to happen. Here is the same file as the engine sees it:

The hello-harness DAG. The ask and check nodes start together from the start rail; report depends on ask; done depends on report and check, with a dashed when-gate on the edge from check.

Figure 1. The hello-harness DAG. Nodes with no dependencies start together; done fires only after both parents succeed and the gate on check’s output holds.

Before running anything, validate. To see what that buys you, first sabotage the file: change depends_on: [ask] to depends_on: [aks] and run:

Terminal window
keelson workflow validate hello-harness
results:
- filename: /Users/you/.keelson/workflows/hello-harness.yaml
ok: false
warnings:
error: Node 'report' depends_on unknown node 'aks'
failed: 1
total: 1

The validator reads the whole graph, so a reference to a node that does not exist is caught before any node executes, with the exact node and the exact bad reference named. Fix the typo back to ask and validate again:

results:
- filename: /Users/you/.keelson/workflows/hello-harness.yaml
ok: true
warnings:
error:
failed: 0
total: 1
Terminal window
keelson workflow run hello-harness --watch
◆ target: cwd=/Users/you
▶ run 7b17995e (hello-harness)
· ask …
· check …
✓ ask
{"status":"ok"}
✓ check
· report …
the agent said: engine answers
✓ report
· done …
all checks passed
✓ done
■ succeeded (2.1s)

Read the trace against Figure 1. ask and check started together because neither has dependencies. report waited for ask, then read its output through the env channel. done waited for both parents, evaluated its gate against check’s JSON, and ran.

One line deserves a second look: report printed the agent’s answer, not your prompt. The ask node asked the model for exactly engine answers, and report surfaced that through the env channel, not a shared chat history. The nodes never share a conversation; they hand each other output, which is what makes a workflow a DAG and not a chat.

You have used two node kinds. There are seven:

NodeWhat it runs
promptOne agent turn against a provider.
bashA shell script, with upstream output in KEELSON_NODE_* env vars.
commandA named, reusable prompt file from ~/.keelson/commands.
loopAn agent turn repeated until an answer matches until:, bounded by max_iterations.
scriptA script file under a declared runtime, such as runtime: bun.
approvalA pause that waits for a human yes or no before downstream nodes run.
cancelA controlled early exit for a branch.

Combined with depends_on, when:, and trigger_rule: for merge semantics, this is the entire vocabulary. Confirm your workflow joined the catalog, alongside the seeded starters:

Terminal window
keelson workflow list
workflows:
- name: fix-issue
description: ...
nodeCount: 24
source: global
- name: hello-harness
description: Three node kinds, one dependency, one gate. A first workflow.
nodeCount: 4
source: global
- name: smoke-test
description: ...
nodeCount: 9
source: global
- ...

The list includes all eight bundled starters plus any workflows you have added. Bundled and user-authored workflows are both reported as source: global in server mode. The path field only appears when the server is down and the CLI runs discovery in-process.

SymptomWhat’s happening
error: no workflow named '...' (exit 4)The name matches no file in ~/.keelson/workflows. Run keelson workflow list to see the catalog, and check the spelling and the .yaml extension.
A prompt node fails with idle_timeoutThe agent turn produced no output for longer than the node’s idle_timeout (30s here). Raise it on the node, or confirm a provider is registered, since an unconfigured provider can stall the turn.
A bash node reads an empty upstream valueDot-access like $ask.output is for when: and prompt text. A bash node reads upstream output from the KEELSON_NODE_<id>_OUTPUT env var (the id’s hyphens become underscores).
A prompt node fails with a Copilot authentication errorCopilot is not signed in for the server running the workflow. Re-run the first-run sign-in; keelson doctor confirms it.

A workflow is a YAML file you can read in one screen: nodes, edges, gates. Validation catches broken graphs before execution, the watch stream shows you the DAG executing in dependency order, and the provider is swappable without touching the file.

A workflow runs the same way every time, but each run still starts from nothing it learned last time. The next page gives the harness a memory: standing context a run can draw on, and conclusions it can hand back to you.

Continue to Make it remember.