Build a Durable-Execution Agent Workflow with LangGraph
Bloomberg reported Temporal is in talks for a roughly $500 million round at a valuation of at least $12 billion, more than doubling its February 2026 $5 billion Series D — durable execution has become the agent backbone. This dispatch builds duragent, a LangGraph workflow with checkpoint-and-replay semantics: every node result persists to Redis, crashes resume from the latest checkpoint, side effects dedupe on step IDs, and a saga node reverses partial work on failure.
Deepak Bagada
CEO, SaaSNext
- Durable execution = persisted state + replay from any checkpoint + idempotent side effects; everything else is scaffolding.
- Checkpointing to your existing store (Redis/Postgres) gives LangGraph most of Temporal's durability without a new platform.
- Step-ID idempotency via SETNX guarantees a replayed charge or email fires exactly once, not once per retry.
- Saga compensation in reverse order turns partial failures into clean rollbacks for multi-step agent runs.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Build a Durable-Execution Agent Workflow with LangGraph
On August 19, 2026, Bloomberg reported that Temporal — the open-source workflow-orchestration platform that has quietly become the backbone for production AI agents — is in talks for a roughly $500 million round at a valuation of at least $12 billion. That is more than double the $5 billion Series D it raised in February 2026, led by Andreessen Horowitz. The fundraising tells you where the industry is heading: durable execution, the property that a workflow's state survives crashes, redeploys, and restarts, is now the default answer to "how do I run an agent that must not lose its place?"
Durable execution rests on three axioms. First, every step's result persists to a store, so a crash mid-workflow does not lose progress. Second, a workflow can replay from any checkpoint — you do not restart the agent, you resume it from the last saved step. Third, side effects are idempotent, so replaying a step that already charged a credit card or sent an email does not double-charge or double-send. Everything else — retries, sagas, compensating actions — is scaffolding on those three axioms. This dispatch builds duragent, a LangGraph workflow that demonstrates all three: every node result is checkpointed to Redis, the graph resumes from the latest checkpoint after a simulated crash, side effects dedupe on step IDs, and a saga node reverses partial work when a multi-step agent run fails. It is a template for any long-running agent — payment flows, ETL migrations, approval chains — and it slots next to the other patterns in the AI workflows library.
Why durable execution is the agent backbone
The 2026 agent stack has a durability problem hiding behind a reliability myth. Demo agents work because they run for twenty seconds inside a single process. Production agents run for hours, span services, and get killed by deploys, autoscaling, and OOMs. If your agent loses its state when the process dies, you have not built an agent — you have built a very expensive retry loop that starts over from scratch every time, and on long tasks it may never finish. Durable execution fixes this by treating workflow state as the source of truth and the process as disposable.
Temporal and LangGraph are converging on the same answer from different directions. Temporal gives you an entire server platform — histories, signals, timers, per-activity retries — while LangGraph gives you a library that checkpoints your graph into a store you already run, such as Redis or Postgres. For teams already in the LangGraph ecosystem, the checkpoint-and-replay pattern below buys most of Temporal's durability without standing up a new platform. The trade-offs are the point of this dispatch: checkpoint granularity, idempotency strategy, and compensation ordering are decisions, not defaults. The latest AI news coverage of the Temporal round is a good background read on why investors are betting on this exact property — at a price tag, in dollar terms, that would buy a small cloud region.
Architecture
flowchart TD
A[entry: read latest checkpoint] --> B{Checkpoint found?}
B -- no --> C[debit wallet]
B -- yes --> D[restore snapshot + seq]
C --> E[reserve inventory]
D --> E
E --> F[capture payment]
F --> G[ship order]
G --> H[complete + final checkpoint]
H --> I[END]
E -- any step failed --> J[saga: compensate in reverse order]
J --> I
Project setup
mkdir duragent && cd duragent
python -m venv .venv && source .venv/bin/activate
pip install langgraph langchain-openai pydantic redis
docker run -d -p 6379:6379 redis:7
# .env
REDIS_URL=redis://localhost:6379/0
CHECKPOINT_NS=duragent:cp
STEP_PREFIX=duragent:step
IDEMPOTENCY_TTL_S=86400
CRASH_AFTER_STEPS=2
SAGA_LEDGER=./saga/log.jsonl
schemas.py
import uuid
from enum import Enum
from pydantic import BaseModel, Field
class StepStatus(str, Enum):
DONE = "done"
FAILED = "failed"
COMPENSATED = "compensated"
class StepRecord(BaseModel):
workflow_id: str
name: str
status: StepStatus = StepStatus.DONE
attempt: int = 0
class Checkpoint(BaseModel):
workflow_id: str
seq: int
node: str
snapshot: dict = Field(default_factory=dict)
created_at: str = ""
class ExecutionState(BaseModel):
workflow_id: str = Field(default_factory=lambda: uuid.uuid4().hex[:12])
seq: int = 0
current_step: str = ""
balance_debited: bool = False
inventory_reserved: bool = False
payment_captured: bool = False
order_shipped: bool = False
log: list[str] = Field(default_factory=list)
tools.py
import os, json, time, hashlib
import redis as redis_db
from schemas import Checkpoint, StepRecord, ExecutionState, StepStatus
db = redis_db.Redis.from_url(os.getenv("REDIS_URL", "redis://localhost:6379/0"))
def retry(fn, attempts=3, backoff=(0.5, 1.0, 2.0)):
last = None
for i, wait in enumerate(backoff[:attempts]):
try:
return fn()
except Exception as e:
last = e
if i < attempts - 1:
time.sleep(wait)
raise last
def write_checkpoint(state: ExecutionState, node: str):
ns = os.getenv("CHECKPOINT_NS", "duragent:cp")
cp = Checkpoint(workflow_id=state.workflow_id, seq=state.seq,
node=node, snapshot=state.model_dump(),
created_at=str(time.time()))
db.set(f"{ns}:{state.workflow_id}", cp.model_dump_json())
def read_checkpoint(workflow_id: str) -> Checkpoint | None:
raw = db.get(f"{os.getenv('CHECKPOINT_NS', 'duragent:cp')}:{workflow_id}")
return Checkpoint.model_validate_json(raw) if raw else None
def idempotent(run_fn, workflow_id: str, step_name: str, payload: dict):
# Step-ID idempotency: the effect fires at most once per step.
step_id = hashlib.sha256(f"{workflow_id}:{step_name}".encode()).hexdigest()
guard = f"{os.getenv('STEP_PREFIX', 'duragent:step')}:{step_id}"
existing = db.get(guard)
if existing:
return json.loads(existing) # replay: return the saved result
result = run_fn(payload)
db.set(guard, json.dumps(result),
ex=int(os.getenv("IDEMPOTENCY_TTL_S", "86400")))
return result
def debit_wallet(payload): return {"charge_id": "ch_9f12"}
def reserve_inventory(payload): return {"reservation": "rsv_77ab"}
def capture_payment(payload): return {"capture": "cap_3001"}
def ship_order(payload): return {"tracking": "TRK-5532"}
def compensate(step: StepRecord, workflow_id: str):
with open(os.getenv("SAGA_LEDGER", "./saga/log.jsonl"), "a") as f:
f.write(json.dumps({"workflow_id": workflow_id,
"compensating": step.name}) + "
")
step.status = StepStatus.COMPENSATED
graph.py
import os
from typing import TypedDict
from langgraph.graph import StateGraph, END
from schemas import ExecutionState, StepRecord, StepStatus
from tools import (write_checkpoint, read_checkpoint, idempotent,
debit_wallet, reserve_inventory, capture_payment,
ship_order, compensate, retry)
class DurableState(TypedDict):
exec: ExecutionState | None
FIELD = {"debit": "balance_debited", "reserve": "inventory_reserved",
"capture": "payment_captured", "ship": "order_shipped"}
def _run_step(state, name, effect_fn, payload):
st = state["exec"]
limit = int(os.getenv("CRASH_AFTER_STEPS", "99"))
if st.seq >= limit:
raise RuntimeError("simulated process crash")
st.seq += 1
st.current_step = name
result = retry(lambda: idempotent(effect_fn, st.workflow_id, name, payload))
setattr(st, FIELD[name], True)
st.log.append(f"{name}:ok:{result}")
write_checkpoint(st, name) # durable point of resume
return st
def entry_node(state):
cp = read_checkpoint(state["exec"].workflow_id)
if cp:
restored = ExecutionState(**cp.snapshot)
return {"exec": restored}
return state
def route_entry(state) -> str:
st = state["exec"]
if any("fail" in line for line in st.log):
return "saga"
if st.order_shipped: return "done"
if st.payment_captured: return "ship"
if st.inventory_reserved: return "capture"
if st.balance_debited: return "reserve"
return "debit"
def debit_node(state):
return {"exec": _run_step(state, "debit", debit_wallet, {"amount": 499})}
def reserve_node(state):
return {"exec": _run_step(state, "reserve", reserve_inventory,
{"sku": "A-42", "qty": 1})}
def capture_node(state):
return {"exec": _run_step(state, "capture", capture_payment, {})}
def ship_node(state):
return {"exec": _run_step(state, "ship", ship_order,
{"address": "12 MG Road, Bengaluru"})}
def saga_node(state):
st = state["exec"]
# Reverse order: ship, capture, reserve, debit.
for name in ("ship", "capture", "reserve", "debit"):
compensate(StepRecord(workflow_id=st.workflow_id, name=name,
status=StepStatus.COMPENSATED), st.workflow_id)
st.log.append("saga:compensated")
write_checkpoint(st, "saga")
return {"exec": st}
def done_node(state):
st = state["exec"]
st.current_step = "complete"
write_checkpoint(st, "complete")
return {"exec": st}
def build_graph():
g = StateGraph(DurableState)
g.add_node("entry", entry_node)
g.add_node("debit", debit_node)
g.add_node("reserve", reserve_node)
g.add_node("capture", capture_node)
g.add_node("ship", ship_node)
g.add_node("saga", saga_node)
g.add_node("done", done_node)
g.set_entry_point("entry")
for n in ("entry", "debit", "reserve", "capture", "ship"):
g.add_conditional_edges(n, route_entry, {
"debit": "debit", "reserve": "reserve", "capture": "capture",
"ship": "ship", "saga": "saga", "done": "done"})
g.add_edge("saga", "done")
g.add_edge("done", END)
return g.compile()
main.py
import asyncio, os, json
from graph import build_graph
from schemas import ExecutionState
async def main():
graph = build_graph()
wid = "wf_durable_demo_0001"
# Run 1: the process crashes after the reserve step.
os.environ["CRASH_AFTER_STEPS"] = "2"
try:
await graph.ainvoke({"exec": ExecutionState(workflow_id=wid)})
except Exception as e:
print("crash observed:", e)
# Run 2: redeployed process, same workflow id -> resumes from checkpoint.
os.environ.pop("CRASH_AFTER_STEPS", None)
result = await graph.ainvoke({"exec": ExecutionState(workflow_id=wid)})
print(json.dumps(result["exec"].log, indent=2))
if __name__ == "__main__":
asyncio.run(main())
How checkpoint and replay works
Every node that mutates state ends by calling write_checkpoint, which serializes the full ExecutionState into Redis under the workflow ID. The seq counter makes each checkpoint a durable, ordered point of resume. At the start of every run — including a restart — entry_node reads the latest checkpoint and restores the snapshot, and route_entry computes the next un-done step from the restored booleans. Crash the process after the reserve step, redeploy, run the same workflow ID again, and the graph picks up at capture: the debit and reserve are never re-run. That is axiom one and two in one loop.
The third axiom, idempotency, lives in idempotent(). Every side effect runs under a step guard keyed by workflow_id:step_name — the step ID. Redis SET ... NX means the effect body executes at most once; a replayed step returns the previously saved result instead of firing the charge or the email again. This is the property that makes replay safe: retrying a crashed payment capture cannot double-charge a customer. When a step genuinely fails and the workflow cannot continue, route_entry detects the failure in the log and sends the state to saga, which writes compensating actions in reverse order — ship, capture, reserve, debit — so a partially completed order books a reversal instead of silently leaking. If you need stronger auditability than Redis offers, swap the checkpoint store for Postgres and keep the same graph shape.
Temporal vs LangGraph: what you are buying
| Capability | Temporal | duragent (LangGraph) |
|---|---|---|
| Durable state | event-history server | Redis/Postgres checkpoints |
| Resume after crash | replay from history | restore latest checkpoint |
| Idempotent effects | activity IDs + signals | step-ID SETNX dedup |
| Compensating actions | sagas / child workflows | saga node, reverse order |
| Operational cost | dedicated platform to run | your existing store |
Cost & decision matrix
The decision matrix below is the cost side of that comparison, and it applies whichever platform you pick.
| Decision | Checkpoint every node | Milestone-only checkpoints | No checkpointing |
|---|---|---|---|
| Resume granularity | exact step | coarse (recompute gap) | none |
| Store writes | one per node | few | zero |
| Crash recovery time | seconds | minutes | restart from 0 |
| Partial-failure risk | low | medium | high |
| When to use | long multi-service agent runs | short bounded tasks | demos only |
Retry Rules & Error Handling
| Failure | Backoff | Fallback | Escalation |
|---|---|---|---|
| Redis unavailable | 0.5s, 1s, 2s | Buffer checkpoint to disk | Alert after 3 attempts; never proceed uncheckpointed |
| Side-effect HTTP 5xx | 1s, 2s, 4s | Idempotent re-run of same step | Fail step, enter saga |
| Duplicate step (replay) | n/a | SETNX dedup returns saved result | none — safe by design |
| Node exception | n/a | Route to saga, reverse order | Post saga summary to Slack |
| Crash mid-write | n/a | Replay from last good checkpoint | Compare checkpoint seq with step ledger |
Testing the workflow
Test the four guarantees in order. Crash-resume: run with CRASH_AFTER_STEPS=2, catch the crash, re-run, and confirm the log shows capture and ship only — never a second debit. Idempotency: replay the ship step twice and confirm one tracking number, because the step guard returns the stored result. Saga: force a capture failure and confirm the saga ledger records ship, capture, reserve, debit compensations in exactly that reverse order. Redeploy: delete the process, restart with the same workflow ID, and confirm entry restores the snapshot from Redis rather than starting over. If the crash-resume test re-runs the debit, your checkpoint write is in the wrong place — the write must land after the state mutates and before the node returns, which is exactly where _run_step puts it.
Closing thoughts
Temporal's march toward a $12 billion valuation is the market pricing in an uncomfortable truth: agents that cannot survive a process crash are toys, and the difference between a toy and a platform is the word durable. duragent shows that LangGraph already has the primitives — checkpoint your state to the store you own, resume from the last good seq, dedupe every side effect on a step ID, and compensate in reverse when the run dies. Applied consistently, that turns an agent from a stateless retry loop into a resumable business transaction. Build the durable habit into every long-running workflow, and pair it with the rest of the patterns in AI workflows — or read up on the orchestration consolidation in the latest AI news before you pick your backbone.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
SpaceX Closes $60B Cursor Deal: Coding-Agent Wars Consolidate
Next Story →Build a Cross-Device Agentic-Commerce Workflow with LangGraph
Related Intelligence Analysis
The Step-by-Step Guide to Automating Meeting Tasks with Whisper
You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...
Lovable AI UI-to-Code Pipeline: 2026 Tutorial
Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...
Claude Code's New Browser: 5 Workflows That Save Hours Daily
Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...