Durability
Durability is a deliberate, per-flow choice — never a silent effect of configuring a store. Name a flow, then make it durable:
remember := bb.NewFlow().WithAgent(a).WithId("remember").Durable()WithId returns a NamedFlow; only a NamedFlow has .Durable(), so a durable flow always has the id it resumes against — durable-but-anonymous won't compile. A flow without .Durable() never persists, even with a store (explicit or the in-memory default) configured; the store is just the backend for the flows that opted in.
A durable flow checkpoints its sub-flows: on a retry with the same X-Run-Id, completed ones replay from their savepoint (a flow.cached trace event) instead of re-asking. .Durable() takes options:
bb.ForwardCompatible()— resume even if the graph changed; by default a changed structure is discarded, not resumed into.bb.Retries(n)bb.TTL(d)bb.ResumeOnReregister()
Use bb.FileStore(dir) to survive restarts — the in-memory default (bb.MemStore()) means durability and triggers work with zero config, but nothing survives a process restart.
What "durable" promises — and doesn't
At-least-once, not exactly-once. A durable run that a client retries with the same run id resumes from the last completed flow; a crash in the narrow window before a result is checkpointed re-runs that flow. Side effects that must not double are the author's responsibility (an idempotency key derived from the run), aided but not guaranteed by the engine.
This extends to response delivery: a resumed run re-delivers every already-delivered Respond stage to the new connection — the crashed connection saw nothing, so the client gets the complete answer again, in order. Re-sent text is harmless; it is exactly the side effects that durability was already promising not to double.
Think of a checkpoint as a save point in a game — the run continues from where it was, not from the top, but "continues" means "at-least-once from that point," not a transactional guarantee across everything the flow touched.
Testing durability
flow.MockStore (an in-memory flow.Store) is available for durability tests — see the package tests under internal/flow for patterns of crashing a run mid-flow and asserting it resumes rather than re-executes.