octomind-workflow · git:20260602.9152274 · 2026-06-02 · sha256 d6b2a08155c626b4

octomind-workflow git:20260602.9152274A

Immutable. This exact content is served forever at /api/v1/blob/d6b2a08155c626b4.

---
name: octomind-workflow
title: "Octomind Workflow Syntax"
description: "Complete syntax reference for octomind workflow files — the external orchestrator that chains octomind run subprocesses. Covers the TOML format, sequential/parallel/loop/conditional step types, variable substitution, session modes, pre-flight validation rules, and the CLI. Activate when authoring, validating, or debugging a workflow .toml file."
license: Apache-2.0
compatibility: "Requires: octomind CLI with the `workflow` subcommand. Workflows are stdin-driven; every referenced role must be an installed role or tap-agent tag."
domains: octomind
rules:
  - content(workflow)
  - file(*workflow*.toml)
  - grep(\[\[steps\]\], *.toml)
---

## Overview

`octomind workflow <file.toml>` is an external orchestrator: it chains multiple `octomind run` invocations into a multi-step pipeline. Each step is an independent subprocess running `octomind run --format jsonl`; outputs flow between steps by name; the final step's output goes to stdout, so workflows compose with shell pipes. This skill is the exact TOML syntax for that file.

Workflows sit above sessions: orchestration lives entirely in this external file, not in any role or config. The former in-session `[[workflows]]` config block and `/workflow` command were removed — `octomind workflow <file.toml>` is the only orchestrator now.

## Mental model

A workflow file is a list of named steps executed top to bottom. A step either runs a role once (sequential), or groups sub-steps that run concurrently (parallel), repeatedly (loop), or by a branch test (conditional). Every step name becomes a `{{name}}` variable holding that step's full text output, available to any later step. `{{input}}` holds the raw stdin. Forward references are rejected before anything runs.

The runtime data flow:

```
stdin ─► octomind workflow file.toml ─► stdout (final step output)
            │  per-step → octomind run --format jsonl (subprocess)
            └─ stderr: per-step progress, cost, tokens, totals
```

## File format

```toml
name        = "my-workflow"        # required
description = "Optional human description"

[[steps]]                          # sequential (the default kind)
name    = "spec"                   # required, unique across the whole file
role    = "developer:general"      # required: installed role or tap-agent tag
prompt  = """
{{input}}

Write a tight implementation spec.
"""                                # required
session = "fresh"                  # "fresh" (default) | "continue"
timeout = 0                        # seconds; 0 = no timeout (default)
retries = 0                        # extra attempts on failure (default 0)
# model = "anthropic:claude-sonnet-4-6"  # optional per-step model override
```

## Step types

A `[[steps]]` table is sequential unless it sets exactly one of `parallel`, `loop`, or `conditional` to `true`. Setting more than one is a hard error.

| Kind | Flag | Required fields | Behaviour |
|------|------|-----------------|-----------|
| Sequential | (none) | `name`, `role`, `prompt` | Runs `octomind run` once with the resolved prompt. |
| Parallel | `parallel = true` | `name`, ≥2 `[[steps.run]]` | Sub-steps run concurrently; next top-level step waits for all. |
| Loop | `loop = true` | `name`, ≥1 `[[steps.run]]`, `exit_when` | Sub-steps run sequentially each iteration until `exit_when` matches or `max_iterations` hit. |
| Conditional | `conditional = true` | `name`, `condition`, ≥1 `[[steps.run]]`, `on_match`/`on_no_match` | Branch: run the sub-step names listed by the matching branch. |

Sub-steps inside `[[steps.run]]` are sequential steps and accept all the same optional fields (`session`, `timeout`, `retries`, `model`).

### Parallel

```toml
[[steps]]
name     = "review"
parallel = true

  [[steps.run]]
  name   = "security"
  role   = "security:owasp"
  prompt = "Security review of:\n{{spec}}"

  [[steps.run]]
  name   = "performance"
  role   = "developer:general"
  prompt = "Performance review of:\n{{spec}}"
```

Sub-steps cannot reference each other — only outer scope. The next top-level step starts after every sub-step completes.

### Loop

```toml
[[steps]]
name           = "refine"
loop           = true
max_iterations = 3                                       # default 10
exit_when      = { output = "tester", contains = "NO ISSUES" }

  [[steps.run]]
  name    = "developer"
  role    = "developer:general"
  session = "continue"
  prompt  = "Implement:\n{{spec}}"

  [[steps.run]]
  name    = "tester"
  role    = "developer:brief"
  session = "continue"
  prompt  = "Verify against spec:\n{{spec}}\n\nCode:\n{{developer}}"
```

`exit_when` is checked between iterations against a named step's output. Omit `output` to test the most recent step. If `max_iterations` is reached without matching, the loop exits with the last iteration's outputs and a stderr warning — the workflow does not fail.

### Conditional

```toml
[[steps]]
name        = "route"
conditional = true
condition   = { output = "spec", contains = "security" }
on_match    = ["deep-dive"]
on_no_match = ["quick-summary"]

  [[steps.run]]
  name   = "deep-dive"
  role   = "security:owasp"
  prompt = "Deep analysis:\n{{spec}}"

  [[steps.run]]
  name   = "quick-summary"
  role   = "developer:general"
  prompt = "One-line summary:\n{{spec}}"
```

`on_match` / `on_no_match` list sub-step names to run. Skipped sub-steps resolve to empty strings in later substitutions.

## Condition shape

`exit_when` and `condition` share one table shape:

| Key | Meaning |
|-----|---------|
| `output` | Step name whose output to test. Omit → most recent step's output. |
| `contains` | Substring match. |
| `matches` | Rust regex match. |

Exactly one of `contains` or `matches` must be set. Regex patterns must compile or pre-flight fails.

## Variable substitution

| Variable | Value |
|----------|-------|
| `{{input}}` | The raw stdin content. |
| `{{step_name}}` | Full text output of a previously completed step. |

Variable names match `[A-Za-z_][A-Za-z0-9_-]*`. A `{{var}}` referencing a step that has not completed before this prompt is a pre-flight error. `input` is reserved and cannot be a step name.

## Session modes

| Mode | Behaviour |
|------|-----------|
| `fresh` (default) | Brand-new session every invocation; no state persists. |
| `continue` | First run sends the templated prompt and remembers the session ID. Subsequent runs (loop iter 2+, or a retry) resume that session — `/done` compresses prior context first, and the templated prompt is replaced with the most recent prior step's raw output. |

The continue-session prompt-replacement rule is what makes the generator↔tester loop work without re-feeding the whole spec each iteration. Each step owns its own session ID; in a loop, `developer` and `tester` accumulate independent histories. The session is ephemeral to a single `octomind workflow` invocation.

## Validation rules

All checked before any step runs (hard-fail):

- File exists and is valid TOML; workflow has at least one step.
- Step names are unique across the whole file, including all sub-steps; non-empty; none named `input`.
- Every `{{var}}` references `input` or a step that completes before the referencing step.
- Parallel: ≥2 sub-steps. Loop: ≥1 sub-step and an `exit_when` with `contains` or `matches`. Conditional: a `condition` with `contains` or `matches`, plus `on_match` and/or `on_no_match` whose names all exist among the block's sub-steps.
- `matches` regexes compile; `exit_when.output` / `condition.output` reference known steps.
- `model`, when set on any step, is non-empty.

Role existence is not checked at pre-flight — an unknown role fails when its subprocess spawns. Verify roles exist before running.

## CLI

```bash
echo "build a JSON-to-CSV CLI in Rust" | octomind workflow myflow.toml   # run
octomind workflow myflow.toml --dry-run                                  # validate + print plan, no spawn, no stdin
```

stdin is required unless `--dry-run`; empty stdin is a hard error. stderr carries each step's assistant message, progress lines, per-step cost/token stats, and the final total. `--dry-run` validates the file, resolves the execution graph, and prints the plan without spawning subprocesses or reading stdin.

## Out of scope

Not supported — use shell composition or call `octomind run` directly: `--var key=value` injection (stdin is the only input), workflow definitions inside `default.toml`, named-workflow lookup (explicit path only), cross-invocation `continue`-session persistence, step artifacts on disk, structured JSON output from the workflow command.

## Examples

### Generator/tester GAN

```toml
name = "gan"

[[steps]]
name   = "spec"
role   = "developer:general"
prompt = "User request:\n{{input}}\n\nWrite an implementation spec."

[[steps]]
name           = "refine"
loop           = true
max_iterations = 3
exit_when      = { output = "tester", contains = "NO ISSUES" }

  [[steps.run]]
  name    = "developer"
  role    = "developer:general"
  session = "continue"
  prompt  = "Implement:\n{{spec}}"

  [[steps.run]]
  name    = "tester"
  role    = "developer:brief"
  session = "continue"
  prompt  = "Verify against spec:\n{{spec}}\n\nImplementation:\n{{developer}}"

[[steps]]
name   = "evaluator"
role   = "developer:general"
prompt = """
Score 1-10:
Spec: {{spec}}
Code: {{developer}}
Verdict: {{tester}}

SCORE: <n>/10
"""
```

### Common mistake — forward reference

```toml
# WRONG: spec references {{review}}, which runs later → pre-flight error
[[steps]]
name   = "spec"
role   = "developer:general"
prompt = "Refine using {{review}}"   # {{review}} not yet available

[[steps]]
name   = "review"
role   = "developer:brief"
prompt = "Review {{spec}}"
```

## Checklist

- [ ] `name` set at top level; every step `name` unique across the whole file; none named `input`?
- [ ] Each sequential step (and sub-step) has `role` and `prompt`?
- [ ] Each `{{var}}` is `input` or a step that completes earlier (no forward refs)?
- [ ] Parallel has ≥2 sub-steps; loop has `exit_when` + `max_iterations`; conditional branch names all exist?
- [ ] `contains` or `matches` set on every `exit_when` / `condition`; regexes compile?
- [ ] Loops bounded by `max_iterations`; long-running steps have a `timeout`?
- [ ] Every referenced role is installed (pre-flight does not check this)?
- [ ] `octomind workflow file.toml --dry-run` prints the expected plan with no errors?

## References

- `octomind workflow <file.toml> --dry-run` — validate and inspect the resolved plan
- Octomind docs: workflows usage guide (`doc/usage/09-workflows.md` in the octomind source)