Run statuses
workflow_design_run returns ok: true with a status, paused runs included. A paused run is a successful call that tells the caller what to do next. ok is false only for a failed run or a refused request.
| Status | Meaning | What to do |
|---|---|---|
done | Finished | Inspect outputs, skipped (nodes gated off by a false condition) and deliveries |
awaiting_approval | A human gate is parked | Ask the user pending.question, then workflow_design_respond with run_id, node_id and answer |
awaiting_agent | An agent step has no performer | Perform pending.prompt yourself, then respond with answer (or structured for JSON conditions can read) |
failed | The run stopped with an error | Read error, including kind and hint when diagnosed; retry deliberately |
Never guess a run_id or a gate id — use the ones in the response.
Approval gates
An approval parks the run with {node_id, question, options, context, waiting_since} and returns control. Resuming validates the answer before mutating state: the answer must be one of the offered options, so a rejected answer leaves the run resumable instead of half-advanced.
The cursor is re-derived from the paused node’s id, not trusted from disk, so editing a design between pause and resume cannot silently skip or repeat the wrong step.
Deadlines live on the pending element rather than the plan, so a run reloaded after a process restart still knows when its gate expires. Nothing sweeps paused runs in the background — reading a run is the moment its deadline is honoured.
Approvals are answered only through the host’s release path. A scheduled run never answers a human gate.
Retries and rate limits
An agent step retries up to its retries budget (0–3) when the spawn seam reports a failure. A failure diagnosed as rate limited gets one extra automatic attempt even past the budget, after a 15-second backoff: a delay is the one thing that clears a 429, and exactly one extra attempt means a second rate limit is reported instead of napping forever.
attempts records how many tries each node took, so a result can explain itself.
Some failures are not retried: a gate that expired says approval timed out, and a step that ran out of retries says what the bot returned. Both are reported, not swallowed.
Retrying a failed run
When the user wants a run to go again, the deliberate path is the offered one: the canvas Retry button, workflow_design_run with retry=<run_id>, or /workflow run retry <run_id>. It re-enters the run at the failed step and reuses the outputs that already succeeded.
- A
failedrun can always be retried. - A
runningrun can be retried only when an earlier retry stamped it and that stamp is at least ten minutes old — otherwise the retry may still be in flight in another process, and taking it over would run steps twice. - A parked gate is answered, not retried. A finished run is not retried.
- Never splice a
run_idinto a fresh Run.
Reading a result honestly
Read the run; do not narrate it. Everything a report needs is in the result.
outputs- Per-node output, keyed by node id
skipped- Nodes a false condition gated off
attempts- How many tries each agent step took
deliveries- Each send attempt as
{node_id, target, ok, detail}— checkokbefore saying a message was sent error{node_id, msg, kind?, hint?}. The optional pair is the diagnosed-failure label and its one-sentence fix; relay both
The Runs view in a canvas lists the same runs from disk, newest first, with a chip for a diagnosed kind and Retry for failed ones.
Invariants worth knowing
- Five node kinds plus note, never a sixth. Edges are
after;x/yare widget-only. - Nothing runs on a timer. Recurring runs are real Hermes cron jobs.
- Host seams are optional and lazy.
profiles,models,spawn,deliveryand the scheduler import host modules inside calls and degrade to “absent”. - A failed delivery does not fail the run; it is recorded in
deliveries. - Untrusted input is re-validated: chat and webhook text goes through the schema before a write, and store paths stay confined under the store root.