Concept · 5 of 7

Workflow

A workflow is a state machine as data. Not code, not a generator, not a coroutine — a definition the runner interprets against the events on a scroll. That one choice is what makes replay exact, authoring portable, and LLMs plausible workflow authors.

Data, not a code path

Most agent frameworks express a workflow as imperative code — a function with loops, branches, and awaits. To re-run it, you re-execute the code. To inspect it, you read the code. To migrate it, you rewrite the code.

Weave goes the other way. A workflow is a compiled definition — nodes, triggers, emits, terminals — stored as data. The runtime ships a single, pure runner that interprets any workflow definition against any scroll. The code path doesn't change between workflows; the data does.

The consequences are not subtle. A workflow you can inspect as a graph. A workflow you can diff between versions. A workflow an LLM can write. A workflow that replays exactly because there is no hidden code-path state — only the scroll has state.

The runner

The runner is a tiny loop — one piece of code for every workflow in the system:

  1. 1
    Read

    Subscribe to the scroll and see every event in order, from offset 0 to the live head.

  2. 2
    Match

    For each event, find the workflow node whose trigger matches — a topic, a guard, a terminal condition.

  3. 3
    Emit

    When a node fires, append the derived event it declares (ai.request, tool.dispatch, workflow.step) back to the scroll.

  4. 4
    Wait

    The runner has no in-memory state of its own. It waits for the next external commit, and the loop repeats.

There is nothing else. No coroutine suspended in memory, no promise chain, no retry controller with its own state machine. The runner's only job is "given this definition and this scroll, decide what to emit next." Restart the process mid-run and the runner picks up from the scroll — not from saved execution context, because there is none to save.

Anatomy

A workflow definition names the steps and what triggers each one. Here's the shape, expressed as plain data:

# A workflow is data. This is the shape; authoring UX
# (Go builders, .weave DSL) compiles down to this.
workflow: greet
nodes:
  - id: ask
    emit: ai.request
    payload:
      prompt: "greet the traveler warmly: {{.input.name}}"
  - id: speak
    on: ai.response
    emit: tool.dispatch
    payload:
      name: speak
      text: "{{.response.text}}"
  - id: done
    on: tool.result
    terminal: true

The authoring UX is sugar on top. Whether you write a .weave file or use the typed builder in Go, the output is the same definition format:

// Typed authoring surface in Go. Compiles to the same
// workflow definition as a .weave file would.
var greet = workflow.Define("greet",
    workflow.Step("ask").Emit(ai.Request{
        Prompt: "greet the traveler warmly: {{.input.name}}",
    }),
    workflow.Step("speak").On(ai.ResponseRef).Emit(tool.Dispatch{
        Name: "speak",
        Text: "{{.response.text}}",
    }),
    workflow.Step("done").On(tool.ResultRef).Terminal(),
)

What the runner does on a scroll

Put the definition and the runner together, and a run looks like this — externals arrive from the outside, deriveds are emitted by the runner in response:

# Same scroll, interpreted. Externals arrive from the outside;
# the runner emits derived events in response.
scroll: run-abc123
[0]  workflow.step    derived      { id: "ask" }
[1]  ai.request       derived      { prompt: "greet …" }
[2]  ai.response      external     { text: "welcome, traveller" }
[3]  workflow.step    derived      { id: "speak" }
[4]  tool.dispatch    derived      { name: "speak", text: "welcome, …" }
[5]  tool.result      external     { ok: true }
[6]  workflow.step    derived      { id: "done" }

External commits are the only thing the runner waits on. Everything else is a consequence it computes. If you have the definition and the externals, the entire trace is reproducible — that is replay.

Why replay is exact, not approximate

Replay works because there is nothing to reproduce that isn't either in the scroll or in the definition. The runner is a pure function of (definition, events-so-far). Feed it the same definition and the same events, and it emits the same next event. Every time.

That is not achievable in frameworks where workflow semantics live inside code paths. Line numbers, retry timers, and implicit async state all drift. Weave moves that state out of the code and into data, where it can be captured, replayed, and proven.

Authoring

Three ways to write a workflow, all producing the same data:

  • The .weave DSL. A purpose-built language with its own LSP and lint rules. Best for workflows you want humans (and LLMs) to read and edit.
  • Typed builders. Native Go, TypeScript, or Python APIs that compile to the same definition. Best when the workflow is co-located with application code that references strongly-typed payloads.
  • LLM-authored. Because workflows are data, an LLM can produce them as text. The runner doesn't care who wrote them as long as they parse — which opens up workflows that are generated, refined, or adapted at runtime.

Status

The workflow runner is the newest major piece of weave and the one where docs lead code most visibly. The definition format and DSL are solid; the full multi-step runner with recovery and workflow-level replay is the active milestone.

Workflow definition (data format)
sdk-shimmed
The shape is stable — nodes, triggers, emits, terminals — but the schema is still tightening as the DSL lands.
Runner (interpreter over scroll)
sdk-shimmed
Single-step runner works today. Full multi-step orchestration with recovery is the next milestone.
.weave DSL + codegen
implemented
.weave files compile to typed services in Go, TS, and Python with LSP support and lint rules.
Typed builders (Go/TS/Py)
sdk-shimmed
Builder APIs exist in Go; TS and Python surfaces are behind the DSL today.
Workflow-level replay
designed
The pieces are in place — deterministic runner plus captured externals — but the end-to-end replay command ships with the full runner.
LLM-authored workflows
designed
Because workflows are data, not code, an LLM can write them as text and the runner will interpret them. The path is open; the tools for it ship later.

Not to be confused with

  • Temporal. Temporal guarantees workflows run to completion across failures — durable execution of code. A weave workflow is a definition the runner interprets; its "durability" comes from the scroll, not from a runtime that remembers where it left off. Different primitive, different guarantee.
  • LangGraph / agent framework graphs. Graph shapes look similar on the surface, but LangGraph executes in memory and treats the graph as code. Weave's graph is data and the runner is a separate, reusable interpreter. That's why weave workflows replay and LangGraph runs don't.
  • A BPMN / YAML workflow engine. Those tools are definition-based too, but aimed at business-process orchestration with human tasks. Weave is purpose-built for AI workflows — the primitives assume model calls, tool dispatches, and replay from captured externals.