May 25, 4-5 AM (6)
May 25, 5-6 AM (14)
May 25, 6-7 AM (17)
May 25, 7-8 AM (17)
May 25, 8-9 AM (32)
May 25, 9-10 AM (43)
May 25, 10-11 AM (64)
May 25, 11-12 PM (33)
May 25, 12-1 PM (43)
May 25, 1-2 PM (40)
May 25, 2-3 PM (20)
May 25, 3-4 PM (27)
May 25, 4-5 PM (16)
May 25, 5-6 PM (6)
May 25, 6-7 PM (9)
May 25, 7-8 PM (15)
May 25, 8-9 PM (13)
May 25, 9-10 PM (16)
May 25, 10-11 PM (44)
May 25, 11-12 AM (26)
May 26, 12-1 AM (12)
May 26, 1-2 AM (11)
May 26, 2-3 AM (8)
May 26, 3-4 AM (11)
May 26, 4-5 AM (6)
May 26, 5-6 AM (9)
May 26, 6-7 AM (26)
May 26, 7-8 AM (43)
May 26, 8-9 AM (39)
May 26, 9-10 AM (42)
May 26, 10-11 AM (45)
May 26, 11-12 PM (59)
May 26, 12-1 PM (34)
May 26, 1-2 PM (50)
May 26, 2-3 PM (50)
May 26, 3-4 PM (18)
May 26, 4-5 PM (20)
May 26, 5-6 PM (13)
May 26, 6-7 PM (20)
May 26, 7-8 PM (12)
May 26, 8-9 PM (15)
May 26, 9-10 PM (15)
May 26, 10-11 PM (35)
May 26, 11-12 AM (30)
May 27, 12-1 AM (16)
May 27, 1-2 AM (8)
May 27, 2-3 AM (9)
May 27, 3-4 AM (5)
May 27, 4-5 AM (32)
May 27, 5-6 AM (9)
May 27, 6-7 AM (49)
May 27, 7-8 AM (65)
May 27, 8-9 AM (38)
May 27, 9-10 AM (74)
May 27, 10-11 AM (83)
May 27, 11-12 PM (30)
May 27, 12-1 PM (50)
May 27, 1-2 PM (39)
May 27, 2-3 PM (53)
May 27, 3-4 PM (37)
May 27, 4-5 PM (11)
May 27, 5-6 PM (18)
May 27, 6-7 PM (21)
May 27, 7-8 PM (25)
May 27, 8-9 PM (17)
May 27, 9-10 PM (15)
May 27, 10-11 PM (29)
May 27, 11-12 AM (27)
May 28, 12-1 AM (9)
May 28, 1-2 AM (3)
May 28, 2-3 AM (5)
May 28, 3-4 AM (2)
May 28, 4-5 AM (9)
May 28, 5-6 AM (34)
May 28, 6-7 AM (31)
May 28, 7-8 AM (84)
May 28, 8-9 AM (33)
May 28, 9-10 AM (54)
May 28, 10-11 AM (50)
May 28, 11-12 PM (21)
May 28, 12-1 PM (46)
May 28, 1-2 PM (50)
May 28, 2-3 PM (23)
May 28, 3-4 PM (43)
May 28, 4-5 PM (86)
May 28, 5-6 PM (13)
May 28, 6-7 PM (31)
May 28, 7-8 PM (43)
May 28, 8-9 PM (34)
May 28, 9-10 PM (17)
May 28, 10-11 PM (36)
May 28, 11-12 AM (32)
May 29, 12-1 AM (12)
May 29, 1-2 AM (13)
May 29, 2-3 AM (4)
May 29, 3-4 AM (3)
May 29, 4-5 AM (0)
May 29, 5-6 AM (2)
May 29, 6-7 AM (5)
May 29, 7-8 AM (16)
May 29, 8-9 AM (37)
May 29, 9-10 AM (34)
May 29, 10-11 AM (69)
May 29, 11-12 PM (25)
May 29, 12-1 PM (44)
May 29, 1-2 PM (66)
May 29, 2-3 PM (60)
May 29, 3-4 PM (25)
May 29, 4-5 PM (26)
May 29, 5-6 PM (79)
May 29, 6-7 PM (11)
May 29, 7-8 PM (19)
May 29, 8-9 PM (9)
May 29, 9-10 PM (8)
May 29, 10-11 PM (27)
May 29, 11-12 AM (7)
May 30, 12-1 AM (9)
May 30, 1-2 AM (2)
May 30, 2-3 AM (1)
May 30, 3-4 AM (5)
May 30, 4-5 AM (2)
May 30, 5-6 AM (11)
May 30, 6-7 AM (0)
May 30, 7-8 AM (2)
May 30, 8-9 AM (11)
May 30, 9-10 AM (13)
May 30, 10-11 AM (10)
May 30, 11-12 PM (5)
May 30, 12-1 PM (8)
May 30, 1-2 PM (5)
May 30, 2-3 PM (18)
May 30, 3-4 PM (5)
May 30, 4-5 PM (1)
May 30, 5-6 PM (9)
May 30, 6-7 PM (9)
May 30, 7-8 PM (1)
May 30, 8-9 PM (5)
May 30, 9-10 PM (4)
May 30, 10-11 PM (27)
May 30, 11-12 AM (12)
May 31, 12-1 AM (17)
May 31, 1-2 AM (0)
May 31, 2-3 AM (1)
May 31, 3-4 AM (1)
May 31, 4-5 AM (0)
May 31, 5-6 AM (0)
May 31, 6-7 AM (7)
May 31, 7-8 AM (4)
May 31, 8-9 AM (10)
May 31, 9-10 AM (3)
May 31, 10-11 AM (4)
May 31, 11-12 PM (4)
May 31, 12-1 PM (1)
May 31, 1-2 PM (2)
May 31, 2-3 PM (24)
May 31, 3-4 PM (15)
May 31, 4-5 PM (2)
May 31, 5-6 PM (1)
May 31, 6-7 PM (2)
May 31, 7-8 PM (2)
May 31, 8-9 PM (2)
May 31, 9-10 PM (7)
May 31, 10-11 PM (25)
May 31, 11-12 AM (11)
Jun 01, 12-1 AM (14)
Jun 01, 1-2 AM (7)
Jun 01, 2-3 AM (3)
Jun 01, 3-4 AM (7)
Jun 01, 4-5 AM (2)
3,626 commits this week May 25, 2026 - Jun 01, 2026
bench | tx-centrifuge: recycle UTxOs by timeout block depth
The RecycleOnConfirm strategy retains a pendingRecycle entry until
the chain follower delivers a confirm or orphan event. If the tx is
silently dropped from the remote mempool (no event ever arrives),
the original inputs sit in pendingRecycle forever and the live UTxO
pool drains, eventually starving the builder.

Add a per-workload timeout sweeper async that ages out entries by
block depth. When a pendingRecycle entry's submission tip block
falls more than `recycle_timeout_blocks` behind the current tip,
the runtime fires an `onTimeout` callback which returns either
TimeoutRecycle (recycle original inputs back to the input queue,
same effect as the orphan path) or TimeoutDrop (give up on this
UTxO entirely). The default `recycle_timeout_blocks = 0` keeps the
sweeper off, so existing configs are unaffected.

The application-level callback in Main bumps a per-UTxO retry
counter and returns Drop once the count reaches the configurable
`recycle_max_retries` (top-level JSON field, default 3). This
bounds the damage from a single hostile or buggy input that would
otherwise re-time-out forever.

Spend-race hazard: if a tx lands on-chain *after* its inputs are
recycled, the follow-up tx the builder produces from those inputs
will be rejected by the node, and the input will re-time-out until
the retry cap is hit. The cap converts an unbounded retry loop
into a bounded one.

Observability:

* New BuilderTimeout trace (namespace TxCentrifuge.Builder.Timeout,
  severity Notice). Fields: builder, txid, retries, action
  (recycle | drop).
* BuilderQueueDepth gains two cumulative counters in its JSON
  payload: recycledTimeout, droppedTimeout. Stats fields are
  populated by the periodic queue-depth tracer async.

Plumbing:

* ObserverHandle gains `ohCurrentBlock :: STM Int`. The N2N and
  N2C TxIdSync state types expose `currentTipBlockNo` to satisfy
  it; the test harness adds a stub.
* Builder gains `timeoutAsync :: Maybe (Async ())` (Nothing for
  strategies without a pendingRecycle map, or when the sweeper is
  disabled).
* New TimeoutAction sum type in PullFiction.Config.Runtime.
bench | tx-centrifuge: recycle UTxOs by timeout block depth
The RecycleOnConfirm strategy retains a pendingRecycle entry until
the chain follower delivers a confirm or orphan event. If the tx is
silently dropped from the remote mempool (no event ever arrives),
the original inputs sit in pendingRecycle forever and the live UTxO
pool drains, eventually starving the builder.

Add a per-workload timeout sweeper async that ages out entries by
block depth. When a pendingRecycle entry's submission tip block
falls more than `recycle_timeout_blocks` behind the current tip,
the runtime fires an `onTimeout` callback which returns either
TimeoutRecycle (recycle original inputs back to the input queue,
same effect as the orphan path) or TimeoutDrop (give up on this
UTxO entirely). The default `recycle_timeout_blocks = 0` keeps the
sweeper off, so existing configs are unaffected.

The application-level callback in Main bumps a per-UTxO retry
counter and returns Drop once the count reaches the configurable
`recycle_max_retries` (top-level JSON field, default 3). This
bounds the damage from a single hostile or buggy input that would
otherwise re-time-out forever.

Spend-race hazard: if a tx lands on-chain *after* its inputs are
recycled, the follow-up tx the builder produces from those inputs
will be rejected by the node, and the input will re-time-out until
the retry cap is hit. The cap converts an unbounded retry loop
into a bounded one.

Observability:

* New BuilderTimeout trace (namespace TxCentrifuge.Builder.Timeout,
  severity Notice). Fields: builder, txid, retries, action
  (recycle | drop).
* BuilderQueueDepth gains two cumulative counters in its JSON
  payload: recycledTimeout, droppedTimeout. Stats fields are
  populated by the periodic queue-depth tracer async.

Plumbing:

* ObserverHandle gains `ohCurrentBlock :: STM Int`. The N2N and
  N2C TxIdSync state types expose `currentTipBlockNo` to satisfy
  it; the test harness adds a stub.
* Builder gains `timeoutAsync :: Maybe (Async ())` (Nothing for
  strategies without a pendingRecycle map, or when the sweeper is
  disabled).
* New TimeoutAction sum type in PullFiction.Config.Runtime.