- Shipped
- August 26, 2026 at 1:59 AM UTC
- Author
- Kamo
- Commit
- 2945f3d
markProcessed was REQUIRES_NEW, so it committed processed=true while the handler's money writes were still uncommitted in the suspended outer transaction. JpaTransactionManager does not flush a suspended persistence context, so the inner commit landed ahead of the outer's pending SQL. A commit-phase failure -- AccountInvoice's unique constraint on stripe_invoice_id, its NOT NULL FK on account_uid, or a swallowed IllegalArgumentException in SubscriptionMemberService.add poisoning the shared transaction -- then rolled the money write back and left the event marked done. Stripe's retry skipped it, so the write was LOST, and it did not even reach the poison-event query because processed was true. markProcessed now joins the outer transaction, so a rollback takes the flag with it and the row stays claimed-but-unprocessed, which is what **************** exists to surface. claim and markFailed keep REQUIRES_NEW: the claim must survive a rollback or every retry looks like a first delivery, and the error detail must survive the rollback it describes. Also strengthens three source-text assertions that a mutation survey defeated: the == 0 gate could be inverted to == 1, the claim could be wrapped in a conditional while index-ordering still passed, and deleting the post-switch markProcessed call was masked by the early-return call site. Found by a three-lens adversarial review; 16 of its 23 findings were refuted.