octomind-workflow — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited octomind-workflow (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
octomind workflow <file.toml|name> is an external orchestrator: it chains octomind run --format jsonl subprocesses into a multi-step pipeline, passing outputs between steps by name. This skill covers how to design a pipeline that is efficient and reliable, and the exact TOML syntax. It applies to any domain.
A workflow is a predefined code path: you fix the steps at authoring time and the orchestrator executes them deterministically. If you cannot enumerate the steps in advance, use a single agent run instead.
Each step starts with a fresh context and knows only what its prompt template passes in — no drift, but also no implicit memory. Every step name becomes a {{name}} variable holding that step's full output; {{input}} is stdin.
stdin ─► octomind workflow file.toml
├─ per step → octomind run --format jsonl (subprocess)
├─ stderr: each step's reply, progress, cost, tokens, totals
└─ stdout: empty, or JSONL events with --format jsonlUse the fewest steps that work. Every handoff is lossy, so the burden of proof is on adding a step.
| Task shape | Pattern | Primitive |
|---|---|---|
| Fixed subtasks, each easier than the whole | Prompt chaining | Sequential |
| Distinct input categories, accurate classifier | Routing | Conditional or graph edges |
| Independent subtasks known upfront | Sectioning | Parallel |
| Same task, diverse perspectives | Voting | Parallel + aggregator |
| N similar items produced by an earlier step | Fan-out | Dynamic parallel (source + match) |
| Output improves against clear criteria | Evaluator-optimizer | Loop |
| Cycles or multi-way routing | State machine | Graph mode |
| Subtasks unknowable before runtime | Orchestrator-workers | Not a workflow — single agent run |
Split only when a different persona, a different model, a gate on the intermediate output, independent verifiability, or parallel speedup demands it. For every boundary, name the artifact that crosses it in one sentence; if it is "the same content, slightly transformed", merge the steps.
Step output is an API. End every prompt with "Output only the <artifact> — no preamble." Gates match substrings or regexes, so the emitting step must produce a machine-checkable sentinel:
VERDICT: APPROVED, not APPROVED.VERDICT: APPROVED / VERDICT: REVISE); never define one as the absence of the other.matches = '(?m)^VERDICT: APPROVED' when precision matters.max_iterations 2–3; worst-case cost is iterations × calls per iteration.session = "continue", iteration 2+ replaces the templated prompt with the most recent prior step's raw output, so the critique reaches the reviser while the session keeps its original instructions.max_iterations exits with the last outputs and a warning; the workflow continues. Gate on the verdict afterwards if an unapproved artifact must not ship.<input>{{prev}}</input> then the task.model: cheap models for extraction, routing, formatting; strong ones for generation and evaluation. Set max_cost to bound runaway spend.name = "my-workflow" # required
description = "Optional description"
# max_cost = 2.5 # USD cap, checked between steps
[[steps]] # sequential (default kind)
name = "outline" # unique across the file
role = "demo:writer" # installed role or tap tag (placeholder shown)
prompt = """
{{input}}
Produce a structured outline. Output only the outline.
"""
session = "fresh" # "fresh" (default) | "continue"
timeout = 0 # seconds, 0 = none
retries = 0 # extra attempts on failure
# model = "<provider>:<model>" # forwarded as --model
# workdir = "path" # resolved against the orchestrator cwd at run time
# skills = ["skill-name"] # forwarded as OCTOMIND_SKILLS
# capabilities = ["websearch"] # forwarded as OCTOMIND_CAPABILITIESUnknown keys are ignored silently, so check spelling against this reference.
A [[steps]] table is sequential unless it sets exactly one of parallel, loop, or conditional to true.
| Kind | Required | Behaviour |
|---|---|---|
| Sequential | name, role, prompt | One octomind run |
| Parallel | name, ≥2 [[steps.run]] | Sub-steps run concurrently; the next step waits for all |
| Loop | name, ≥1 [[steps.run]], exit_when | Sub-steps repeat until exit_when matches or max_iterations (default 10) |
| Conditional | name, condition, ≥1 [[steps.run]], on_match and/or on_no_match | Runs the listed sub-steps; skipped ones resolve to empty strings |
Sub-steps accept the same optional fields as sequential steps. Parallel sub-steps cannot reference each other.
Parallel extras: count = N (N ≥ 2) on a sub-step replicates it; max_parallel caps concurrency; min_success (1..total replicas) relaxes the all-must-succeed default. Dynamic fan-out: exactly one template sub-step plus source = "<earlier step>" and match = "<regex with one capture group>". Each match spawns a branch; inside it {{<block name>}} is that branch's captured item, and the accumulated output lands under the sub-step's name.
Setting entry = "<step>" or any [[edges]] switches to explicit routing. Edges have from, to, and optional when (condition shape); the first matching conditional edge wins, and each node needs exactly one unconditional edge declared last. max_transitions (required) bounds total hops, and routing ends at $end. Every node must be reachable with a route to $end. Referencing an output that the current route never produced fails at run time.
exit_when, condition, and edge when take output (step name, default the previous step), plus exactly one of contains (substring) or matches (Rust regex).
{{input}} — stdin; {{step_name}} — output of an earlier step. Names match [A-Za-z_][A-Za-z0-9_-]*; input and $end are reserved.{{DATE}}, {{CWD}}, {{SHELL}}, {{OS}}, {{BINARIES}}, {{ROLE}}, {{SYSTEM}}, {{CONTEXT}}, {{GIT_STATUS}}, {{GIT_TREE}}, {{README}}.<context>path</context> or <context>path:start:end</context> block in a prompt is replaced with that file's contents.Checked before any step runs: valid TOML with at least one step; unique, non-empty step names; every {{var}} is a builtin, input, or an earlier step; parallel ≥2 sub-steps (or one template with source+match); loop has exit_when; conditional branches name existing sub-steps; regexes compile; model/workdir non-empty when set; count only on parallel sub-steps; max_cost positive; graph constraints above. Not checked: role existence for local files (a bad role fails when its subprocess spawns) and workdir existence.
Workflows fetched from a tap are additionally restricted to public domain:spec tap roles, so they stay portable.
octomind workflow # list tap workflows
octomind workflow myflow.toml --dry-run # validate + print plan, no spawn, no stdin
echo "notes..." | octomind workflow myflow.toml # run a local file
echo "..." | octomind workflow <name> # run a tap workflow
echo "..." | octomind workflow myflow.toml --format jsonl # assistant event per step + final cost event on stdoutAn argument containing / or ending in .toml is a file; anything else is looked up in taps' workflows/. stdin is required and must be non-empty unless --dry-run. Without --format jsonl stdout is empty: each step's reply, progress, and the totals go to stderr. To consume the final result programmatically, use --format jsonl and take the last assistant event.
Inside a session: /workflow <name> <input> runs a tap workflow; agents with the orchestration capability can use tap(action="workflow").
Retries rerun the whole step — keep retries = 0 on steps whose role writes files or calls external APIs unless they are idempotent.
name = "report-with-review"
[[steps]]
name = "draft"
role = "demo:writer"
prompt = "<input>\n{{input}}\n</input>\n\nWrite the report. Output only the report."
[[steps]]
name = "refine"
loop = true
max_iterations = 3
exit_when = { output = "critic", matches = '(?m)^VERDICT: APPROVED' }
[[steps.run]]
name = "reviser"
role = "demo:writer"
session = "continue"
prompt = "Improve this report:\n{{draft}}\n\nOutput only the report."
[[steps.run]]
name = "critic"
role = "demo:critic"
session = "continue"
prompt = """
Review against this rubric only: claims sourced; structure follows brief; no filler.
Critique failing items specifically. If every item passes, approve — do not invent criteria.
<report>
{{reviser}}
</report>
End with exactly one line: VERDICT: APPROVED or VERDICT: REVISE.
"""Iteration 1 sends both templated prompts; from iteration 2 the continue rule feeds the critique to the reviser and the revision to the critic.
name = "fan-out"
[[steps]]
name = "plan"
role = "demo:planner"
prompt = "List the sub-questions for: {{input}}\nOne per line as: Q: <question>"
[[steps]]
name = "q"
parallel = true
source = "plan"
match = '(?m)^Q: (.+)$'
max_parallel = 4
[[steps.run]]
name = "answers"
role = "demo:researcher"
prompt = "Answer concisely: {{q}}"[[steps]]
name = "outline"
role = "demo:writer"
prompt = "Refine using {{review}}" # review runs later → pre-flight erroroctomind run do this? Every step justified by a splitting gate?model chosen per step; max_cost set for open-ended runs?octomind complete run) and each step tested standalone?--dry-run clean; result consumed via --format jsonl when scripted?octomind workflow <file.toml> --dry-run — validate and inspect the resolved plandoc/usage/09-workflows.md; config-templates/workflow*.toml in the octomind source~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.