Counting visitors…
Browse all docs
Start · 9Find your path through RunifoldLearn Runifold in 45 minutesUnderstand the complete Runifold platformYour first trustworthy runChoose the right execution APIChoose crates and Cargo featuresBuild common Runifold applicationsRunifold frequently asked questionsTroubleshoot Runifold applications
Execution kernel · 7Understand RunContextCoordinate external effects safelyBound work with budgets and cancellationHandle errors and retries safelyEvents, journals, and execution evidenceDesign capability-safe executionRecover safely from checkpoints
Models & providers · 7Route across models without duplicate outputChoose and configure a providerUse the provider-neutral model protocolBuild on the Provider Runtime contractUse OpenAI control-plane and Realtime APIsTest and benchmark provider adaptersSet up OpenAI, Anthropic, Gemini, and Ollama
Agents · 7Build and configure an AgentGive an Agent typed toolsAdd conversations and semantic memoryDelegate to child Agents safelyReturn structured Rust valuesStream without losing semanticsGround an Agent with retrieval
Durable workflows · 7Compose deterministic workflowsMake workflows durableOperate durable workflow workersCoordinate timers, signals, and durable waitsRun multi-tenant workflow infrastructureRun parallel branches and safe racesVersion and evolve durable workflows
Integrations · 7Connect through MCPChoose stores and persistence boundariesExpose durable work through MCP TasksBuild and evaluate retrieval pipelinesUse MCP Resources, Prompts, and SamplingCache MCP responses without crossing authorityDeploy Runifold in a Rust web service
Quality & operations · 10Test without the networkEvaluate quality and prevent regressionsObserve the complete run treeRun safely in browsers and at the edgeRead reliability claims preciselyRun reproducible evaluations in CIOperate Runifold with SLOsGovern Task retention and deletionArchive audit evidence to S3-compatible WORM storageManage compatibility and trusted releases
Docs/Workflows
NEW TO RUNIFOLD?Build the complete mental model in 45 minutes
Workflows

Compose deterministic workflows

Connect typed steps, parallel branches, and first-success races without asking a model to control every transition.

Practical guide·18 min

Agent or workflow?

Use an Agent when the next action genuinely needs model judgment. Use a workflow when transitions are business rules you can express deterministically. Most production systems combine both: deterministic orchestration around bounded Agent steps.

This keeps approval, compensation, fan-out, and completion rules in typed code instead of hiding them in a prompt.

Install and run the smallest workflow

Workflows are included in the default runtime feature. Start with a provider-free step so you can understand orchestration before adding model calls.

Cargo.toml
[dependencies]
anyhow = "1"
futures-executor = "0.3"
runifold = "=0.9.0"
serde_json = "1"
src/main.rs
use anyhow::Context;
use runifold::{
    Budget, BudgetTracker, CapabilitySet, RunContext, Workflow, WorkflowStep,
    WorkflowStepError, WorkflowStepFuture,
};
use serde_json::{Value, json};
 
struct NormalizeOrder;
 
impl WorkflowStep for NormalizeOrder {
    fn execute<'a>(
        &'a self,
        input: Value,
        _run: &'a RunContext,
    ) -> WorkflowStepFuture<'a> {
        Box::pin(async move {
            let order_id = input["order_id"]
                .as_str()
                .ok_or_else(|| WorkflowStepError::Execution("missing order_id".to_owned()))?;
            Ok(json!({ "order_id": order_id, "normalized": true }))
        })
    }
}
 
fn main() -> anyhow::Result<()> {
    let workflow = Workflow::builder("order-intake")
        .version(1)
        .step("normalize", NormalizeOrder, CapabilitySet::new())
        .build()
        .context("failed to build workflow")?;
    let run = RunContext::root(
        BudgetTracker::new(Budget::default()),
        CapabilitySet::new(),
    );
    let outcome = futures_executor::block_on(
        workflow.run(json!({ "order_id": "ord_42" }), &run),
    )?;
 
    println!("{}", outcome.output);
    Ok(())
}

Run it with cargo run. The output of one step becomes the input of the next; outcome.output is the final canonical value and outcome.usage contains the budget actually consumed. A build failure means the graph is invalid; a run failure means a step, capability, budget, deadline, or cancellation boundary stopped execution.

Sequential steps

A workflow definition gives every step a stable StepId, typed input/output, and an explicit failure policy. Pass only the data needed by the next step.

Good step boundaries are independently retryable and observable: fetch a record, classify it, request approval, apply an effect, persist the result. Avoid one giant step that mixes all five.

Parallel branches

Parallel branches reduce latency when work is independent. Reserve budget before launching them and cap concurrency at the worker or tenant boundary.

Choose a join rule explicitly: require all branches, accept a subset, or continue with a typed partial result. A slow branch must not silently expand the overall deadline.

First-success race

A first-success race is useful for redundant reads or replaceable model routes. The first valid result wins and losing branches are cancelled.

Do not race non-idempotent writes. Cancellation cannot retract an external effect that already reached its destination.

Add an Agent step

Build Agents first, wrap them in Arc, and grant each step only the capabilities it needs. Stable step IDs such as plan and write become durable identity, so do not rename them while old checkpoints may resume.

let workflow = Workflow::builder("plan-and-write")
    .version(1)
    .agent("plan", planner, CapabilitySet::new())
    .agent("write", writer, CapabilitySet::new())
    .build()?;
 
let outcome = workflow.run("Design a retry policy", &run).await?;
println!("{}", outcome.output);

Use a regular .step(...) for deterministic Rust logic and .agent(...) only where model judgment is required. This keeps validation, writes, approvals, and state transitions outside prompts.

Add review-gated generation

Use .repairable_agent(...) when an application-owned reviewer must approve a generated value before the workflow commits it:

let workflow = Workflow::builder("reviewed-answer")
    .repairable_agent(
        "draft",
        answer_agent,
        compliance_reviewer,
        WorkflowRemediationPolicy::new(2),
        generation_capabilities,
        reviewer_capabilities,
    )
    .build()?;

The first generation receives ordinary workflow input. A repair verdict persists the rejected candidate and structured feedback before the next generation. Generation, review, approval and repair are separate write-ahead substages, so recovery continues from a durable candidate without generating it again. Draining 0.7 workers before a 0.8/0.9 checkpoint writer is mandatory: schema v5 can be read by 0.9 workers but not by 0.7 workers.

Choose the execution pattern

RequirementBuilder shapeImportant constraint
every stage depends on the previous resultchained step / agent callskeep each output serializable
all independent results are requiredparallelreserve a bounded budget for every branch
the first valid read is enoughraceonly Pure or ReadOnly capabilities
work must survive process lossdurable store + workerversion definitions and stable IDs
a person or timer must resume workdurable wait / signalauthenticate and deduplicate signals

Debug a failed workflow

  1. Build the workflow at startup and fail immediately on duplicate or invalid IDs.
  2. Log the workflow name, definition version, step ID, Run ID, and typed error kind.
  3. Inspect outcome.usage and the Run journal before assuming the provider failed.
  4. If an external write may have completed, reconcile it; do not blindly rerun.
  5. For durable execution, verify the deployed worker still registers the checkpoint's exact workflow version.

Continue with durable workflows when a run must survive restart, parallelism for fan-out/race semantics, and workers for queue and lease operation.