Skip to main content

Hand-offs and routing

Different steps in a workflow can run on different agents. This page covers how steps pass results to each other, and how each step chooses its harness (runtime) and model.

Hand-offs: declare outputs, don't pass chat history​

An upstream node declares stable results in outputs. A downstream node lists them in inputs, and can reference only outputs that its direct upstream nodes declare.

{
"key": "understand",
"type": "ai",
"name": "Understand and plan",
"config": { "prompt": "Write a plan to deliverables/plan.md" },
"outputs": [
{ "key": "plan", "kind": "file", "path": "deliverables/plan.md", "label": "Plan", "required": true }
]
}
{
"key": "implement",
"type": "ai",
"name": "Implement",
"dependsOn": ["understand"],
"inputs": [
{ "key": "plan", "fromNode": "understand", "outputKey": "plan", "label": "Approved plan", "required": true }
],
"config": { "prompt": "Implement and self-test from the plan" },
"outputs": [ { "key": "change", "kind": "git", "label": "Commit", "required": true } ]
}

At run time, SoduWork builds a bounded snapshot for each hand-off. It records the input key, source node, output key, checksum and whether anything was truncated. Agents don't share terminal history, and old conversations are not passed whole to the next model.

Output kinds​

kindContainsChecked by
fileA file at an exact pathFile name and presence
gitA commitFor engineering work, deliver Git, not an archive
evidenceA check result, screenshot or other proof
urlAn address where the result can be opened
decisionA structured verdictUTF-8 JSON; at most one per node

Decision files​

An ai_review node writes a decision file. Use a .json path for new plugins.

{
"schemaVersion": 1,
"outcome": "proceed",
"reasonCode": "ready",
"summary": "Checked each item against the source material",
"evidence": ["Source record, paragraph 2, matches notes section 3"],
"nextAction": ""
}

When a check fails or can't be completed, the review must return "outcome": "blocked", a reasonCode other than ready, and a concrete nextAction. A blocked review stops the run.

  • An ai_review of files must declare a required decision.
  • A code review also needs a fixed Git version and code-review.json.

Neither kind of AI review replaces a later human approval.

Routing: which harness and model runs each step​

A plugin can suggest a route in a node's config:

"config": {
"prompt": "Review the plan independently",
"agentKind": "local:qwen",
"model": "deepseek-chat",
"fallbackMode": "strict",
"runnerTags": ["gpu"]
}
FieldNotes
agentKindlocal:<harness id> for a local harness, for example local:pi or local:qwen. Empty means the organization default
modelThe model name sent to the gateway. Empty means the harness's default
fallbackModestrict: use exactly this harness. best-effort: pick a compliant alternative if it's unavailable
runnerTagsRun only on runners with these tags (up to 12)

It's usually better to leave the concrete choice to the binding. Then the same plugin can use Codex in one company, Pi in another and OpenCode on an isolated network, without copies.

  1. The binding sets the default harness, model, unavailable policy and runner tags for the whole workflow.
  2. Each ai and ai_review node can override them.
  3. Before a real start, a node-by-node simulation shows "expected route → runner actually online".
  4. Every run records the expected route, the harness, model and host actually used, and any fallback reason.

Don't use the fields agent, agentId, adapterId or runtime. They are rejected.

Common patterns​

  • Build with one model, review with another. Give ai and ai_review nodes different routes so a review is never self-review.
  • Cheap model for bulk steps, strong model for the key step. Use a small model to extract and sort; use a strong one to judge or review.
  • Keep sensitive data local. Pin the step that handles identities to a local harness and model; send only de-identified outputs to a cloud model.
harness harnesses runtime runtimes 底座
Edit this page