Split out of #4885 per review feedback on #4933, which contributes the failing proof test.
What happens
During startup there is a small window between "process previously received (mailbox) messages" and "trade protocol listeners are registered for live messages". A trade protocol message that completes processing inside that window is acknowledged at the network layer — the sender's MessageDeliveryStatus reads delivered, the mailbox entry is cleared, and the automatic resends never fire — but the trade protocol never observes it.
The trade then sticks permanently: when the peer's follow-up message arrives (e.g. "payment sent" waiting on the earlier Bitcoin-address message), it is parked waiting for a state transition that can no longer happen. Nothing recovers it, because from the network's point of view nothing was lost.
Repro
BisqEasyTradeStuckByOutOfOrderMessageProofTest#tradeAdvancesWhenEnablingMessageCompletesProcessingDuringStartupHandoffWindow (#4933) — fails on main: the trade remains in the earlier state instead of advancing.
The control test in the same class (control_outOfOrderFiatSentRecoversWhenEnablingMessageStillArrivesLive) shows the live-arrival path recovering fine, isolating the startup handoff as the loss mechanism.
Direction
The invariant to establish: a message must not be ACK-consumed before the consumer that owns it can process it. Either register trade protocol listeners before message dispatch starts, or buffer/replay messages that complete processing during the handoff so listener registration re-delivers them. The failing test is the acceptance criterion; the control test must stay green.
Impact
One of the two mechanisms behind trades stuck mid-protocol reported in the field (bisq-network/bisq-mobile#1622). Sibling issue: #4946
Split out of #4885 per review feedback on #4933, which contributes the failing proof test.
What happens
During startup there is a small window between "process previously received (mailbox) messages" and "trade protocol listeners are registered for live messages". A trade protocol message that completes processing inside that window is acknowledged at the network layer — the sender's
MessageDeliveryStatusreads delivered, the mailbox entry is cleared, and the automatic resends never fire — but the trade protocol never observes it.The trade then sticks permanently: when the peer's follow-up message arrives (e.g. "payment sent" waiting on the earlier Bitcoin-address message), it is parked waiting for a state transition that can no longer happen. Nothing recovers it, because from the network's point of view nothing was lost.
Repro
BisqEasyTradeStuckByOutOfOrderMessageProofTest#tradeAdvancesWhenEnablingMessageCompletesProcessingDuringStartupHandoffWindow(#4933) — fails on main: the trade remains in the earlier state instead of advancing.The control test in the same class (
control_outOfOrderFiatSentRecoversWhenEnablingMessageStillArrivesLive) shows the live-arrival path recovering fine, isolating the startup handoff as the loss mechanism.Direction
The invariant to establish: a message must not be ACK-consumed before the consumer that owns it can process it. Either register trade protocol listeners before message dispatch starts, or buffer/replay messages that complete processing during the handoff so listener registration re-delivers them. The failing test is the acceptance criterion; the control test must stay green.
Impact
One of the two mechanisms behind trades stuck mid-protocol reported in the field (bisq-network/bisq-mobile#1622). Sibling issue: #4946