Skip to content

Support replay-derived step arguments without persisting full inputs #4238

Closed

Description

@AndrewBarba

Long-running workflows often reconstruct growing state from completed step results and pass that state into the next step. Even when each step returns only a small delta, Workflow persists the full input on every boundary. With a fixed amount of new state per step, the accumulated input payload grows quadratically.

let state = initialState;
while (hasMoreWork(state)) {
  const delta = await advance(state);
  state = applyDelta(state, delta);
}

The workflow body already recomputes arguments during replay. The missing capability is supplying selected reconstructed arguments to the executing step without persisting them again.

Proposal

Add an explicit per-step setting, following maxRetries:

async function advance(state: State, execution: ExecutionInput) {
  "use step";
  // Return the changes produced by this step.
}

advance.replayInputs = [0];

Indices select arguments in the original function signature. Selected arguments contain plain state data; unselected arguments, captured closures, and this retain existing persistence. The default behavior is unchanged.

Persist versioned markers and integrity fingerprints for selected inputs. Capture their values at invocation time, pass isolated materialized values to the executor in memory, and keep their contents out of durable events and queue messages. Cold execution and retries must replay to the targeted invocation, reconstruct the arguments, and verify their fingerprints before executing. Completed steps continue returning their recorded results without re-execution.

Support both Node and QuickJS. Preserve step identity, ownership, retry delays and limits, cancellation, and parallel execution semantics. Input inspection should identify values as reconstructed through replay.

Boundaries and validation

This is an input-persistence capability, not automatic state diffing or a new storage/retrieval API. Output deltas remain application-owned. Live handles and custom serialization use ordinary persisted arguments. Cold retries incur workflow replay; normal materialization still costs CPU.

Validate cold restart, interrupted execution, delayed retries, retained VMs, concurrent wakes, parallel steps, mutation after invocation, bound arguments, mixed argument modes, and mismatched fingerprints in both engines. A growing-state benchmark should show linear durable bytes when paired with fixed-size output deltas.

Related: #3026 describes accumulated-history performance costs. This proposal specifically removes repeated step-input state from durable storage and replay transfer.

Activity

  1. VaguelySerious commented on Nov 13, 2026

    @VaguelySerious
    Member

    Fixed by #5215

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions