This is very cool. So I thought through there's definitely a need here to
pause mid-action.
Some API ideas:
1. Put durable_keys as part of the declaration to match the type-safe,
declarative nature of the rest of it
2. Use something clever (i.e. match statements) to make it easier to match
the mental model
Azure durable functions has an interesting approach:
def my_action(state, ctx):
user = yield ctx.durable("fetch", db.get_user, state["uid"])
match user.tier:
case "gold":
result = yield ctx.durable("gold_flow", gold_flow, user)
case _:
result = yield ctx.durable("std_flow", std_flow, user)
return state.update(result=result)
Worth exploring. I'm also wondering if there's a simple way to add an
optional field to the data model rather than a whole new "supports
durable"? I.E. would there be an easier way to onboard? Could we model it
as some sort of sub-state that we could store and pass in? If we had
durable things as a special key-space in the state could we just use the
same ones? Trade-offs not sure the right one.
The problem is that if we don't have suspend() we decouple it but need a
mechanism to halt execution. Might be a good place for syntactic sugar --
halt_during? halt_when(durable=["key"])?
With iterator-based APIs it's actually up to the user to handle the halting
-- this completely bypasses this issue (we just send out a durable-set
event mid-stride as we would an iterator or an iterator of streams).
On Sun, May 31, 2026 at 11:45 AM Stefan Krawczyk <[email protected]>
wrote:
> If halt_when has clear semantics if what happens next then that sounds
> good. But I haven't found a clear way to make that obvious. So I think
> making users set up state and conditional edges should be the cleaner model
> (we should also show that running a graph with halt* is fine in examples
> and drop that warning).
>
> Durable key seems fine I think. I think the hierarchical nature should be
> utilized that we've set up, so more capabilities to enable that way of
> pausing and resuming to be more useful makes sense to me -- IIUC.
>
> On Thu, May 28, 2026, 6:14 AM André Ahlert <[email protected]> wrote:
>
> > Hi all,
> >
> > PR #786 <https://github.com/apache/burr/pull/786> [1] proposes two
> > additions to handle long-running agent workloads. Moving the design
> > question from DM with Stefan onto the list before we iterate further.
> >
> > Gap today: halt_before / halt_after only stop between actions. Two cases
> > keep showing up:
> >
> > 1. HITL inside one action (e.g. sequential batch where item 23 of 50
> > needs approval, items 1-22 already paid for LLM calls).
> > 2. Crash recovery mid-action without re-firing paid sub-steps.
> >
> > Langgraph covers both with interrupt() + checkpointer. Concrete case I am
> > hitting: a loyalty CRM where every retry re-fires the offer-generation
> LLM.
> >
> > Proposal is to split #786 into two orthogonal pieces, ship independently:
> >
> > * A. *halt_when(predicate): state-based HITL primitive. Action sets a
> state
> > value, runtime halts when predicate matches, conditional edges route on
> > resume. No new resume semantics. (Stefan's suggested shape on the PR.)
> >
> > * B. *__context.durable(key, fn): sub-step memoization journal. Result
> > keyed by (app_id, sequence, key). On replay, returns recorded value
> without
> > re-firing fn. Defensible on crash recovery alone.
> >
> > Worked example (before/after code, mid-action case discussion):
> > https://gist.github.com/
> >
> > Open questions:
> >
> > 1. halt_when(predicate) as first-class transition primitive, or keep
> as
> > user-side conditional edge expression?
> > 2. __context.durable(key, fn) the right surface, or decorator?
> > 3. Anyone hitting the mid-action sequential case in production?
> >
> > Not proposing the suspend()-style mid-action pause in this thread. Want
> to
> > ship A and B, collect signal, revisit on a separate [DISCUSS] if demand
> > shows up.
> >
> > No deadline. Will follow up with [PROPOSAL] per piece once we converge.
> >
> > [1] https://github.com/apache/burr/pull/786
> >
> > Thanks, André
> >
> >
>