Skip to content

langclaw 0.3.0 — durable workflows

0.3.0 makes workflows properly durable: steps survive crashes, incomplete runs resume on restart, and fan-out failures are isolated rather than fatal.

Crash-resume

Each step in a @app.workflow is now a memoized, serializable unit backed by a RunStore. If the process dies mid-run — between ctx.parallel branches, mid-synthesis, anywhere — the run picks up from the last completed step on next startup.

@app.workflow("digest", input=DigestInput, max_concurrency=4)
async def digest(ctx, inp: DigestInput) -> str:
    ctx.phase("fetch")
    # If the process crashes here, this fan-out won't re-run on resume.
    items = await ctx.parallel([
        lambda c, url=url: c.tool("web_fetch", url=url)
        for url in inp.sources
    ])

    ctx.phase("summarize")  # resumes here if fetch already completed
    ...

Opt into startup resume with:

LANGCLAW__WORKFLOWS__RESUME_ON_STARTUP=true

On startup, langclaw scans the RunStore for incomplete runs and re-triggers them before the gateway starts accepting new messages.

ctx.log — inline progress

A lightweight log line to narrate what's happening inside a phase, without starting a new one:

ctx.phase("verify")
for claim in claims:
    ctx.log(f"checking: {claim[:60]}…")
    result = await ctx.tool("web_search", query=claim)

Both ctx.phase and ctx.log stream live to the originating channel as the workflow runs.

Parallel failure isolation

ctx.parallel now accepts return_exceptions=True — a single failing branch returns an Exception in place instead of sinking the entire fan-out:

results = await ctx.parallel(
    [lambda c, name=name: c.subagent("scout", name) for name in contenders],
    return_exceptions=True,
)

for name, result in zip(contenders, results):
    if isinstance(result, Exception):
        ctx.log(f"{name}: research failed, skipping")
        continue
    ...

What's in 0.3

  • Workflow durability: RunStore protocol, serializable-input guard, per-step memoization
  • ctx.log — inline log lines during a phase
  • ctx.parallel failure isolation (return_exceptions=True)
  • Startup resume trigger for incomplete runs (resume_on_startup)