Start a batch partly filled
Items already satisfied before the wait begins go in filled, so the row opens with real progress and delivers them with the rest.
The situation
The cut station finished before the assembly workflow started waiting. Opening the batch with cut still pending would ask someone to submit work that is already done.
The recipe
const parts = await conditional<Record<'cut' | 'weld' | 'paint', StationResultV1>>(signalId, { role: 'assembly', metadata: { orderId }, batch: ['cut', 'weld', 'paint'], filled: { cut: { payload: { station: 'cut-1', ok: true }, at: cutRecord.completedAt, actor: cutRecord.userId }, },});Each entry in filled carries its payload, the at it was satisfied, and an optional actor. Take at from the source of record, never a clock read in the workflow, so replay folds the same row.
The guarantee
The row starts with those keys left out of batch_pending, and their payloads, times and actors are written beside the rest. The completing wake delivers them with the collection. Every key in filled must be declared in batch, and at least one must stay open: a filled that covers every key throws, so skip the wait instead. See Batches and accumulators.
Related patterns
The escalation boundary
Batch: N contributions, one wake
One row collects a named set of submissions and wakes the workflow once, when the last one lands, with the whole collection.
The escalation boundary
Deliver what arrived on timeout
With partialOnTimeout, a batch that misses its deadline wakes with the items filled so far instead of false, so the workflow can act on them.
The escalation boundary
One crash-safe write
The row, its facets and the resume timer commit with the workflow's checkpoint in one write. A crash leaves all of it or none of it.
