- Shipped
- September 8, 2026 at 7:47 AM UTC
- Author
- Kamo
- Commit
- f23167c
Two defects found by reading the code back, neither of which any test could see. ## The clock would have thrown on its first tick `HoldemDeadlineSweep` carried `@Transactional` on three PROTECTED methods it called on `this`. Spring's proxy never sees a self-invocation, so the annotation did nothing but read as though a transaction were open — and both conditional claims are `@Modifying` queries, which throw `TransactionRequiredException` without one. Every tick, once a second, forever: no hand would ever have been dealt and no player would ever have been timed out. `@Transactional` now sits on the two repository methods, which is where a `@Modifying` query actually needs it and where a self-invocation cannot lose it. The sweep's three helpers are private and say why they carry no annotation. ## A full-sized all-in re-opened nothing `actedThisStreet` decided what had re-opened the betting from the action's NAME — BET or RAISE. But a raise that leaves the raiser with nothing is recorded as ALL_IN, deliberately: the interesting property of an all-in is that the player can never act again, and every consumer cares about that and nothing else. So a full-sized all-in raise looked like "not a raise", and a player facing a legitimate re-raise could only call or fold. `replayStreet` now walks the street with the engine's own arithmetic — aggression is putting in more than the outstanding bet; a FULL raise is an increment of at least the previous raise size or the big blind. A short all-in moves the bet and re-opens nothing, which is the rule. Blinds set the bet to beat without counting as aggression, which is what keeps the big blind's option. `fullSizedAllInReopens` is the counterpart to the short-all-in test, and pins the minimum for the raise after it.