Facing more than you have left is a call for your whole stack — commit() clamps
the commitment — so labelling it "Call 35" when only 20 can be paid promised a
price the player could neither make nor owed. It now reads "All in 20".
The ViewModel also submits the clamped amount rather than relying on the engine
to fix it up, so the action sent never disagrees with the label shown.
Verified by reverting: both new label tests fail.
Tests: 141 -> 144 (64 engine JVM, 64 Android host, 16 app).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1. Raise could vanish when it was legal. The action bar required
maxRaiseTo > minRaiseTo, which hid an exact-minimum raise and any legal short
all-in — both of which the engine accepts (sizeBet clamps a stack too short
for a full min-raise to maxRaiseTo). The button now shows whenever the engine
would accept a raise; the slider only appears when there is a genuine range,
and a single-size raise submits a fixed amount.
2. The header used handsPlayed + 1, which increments when a hand *finishes*, so
during the showdown hold it labelled hand 1's result as "Hand 2". It now uses
snapshot.handNumber, which is by construction the hand being displayed.
3. Decision identity is now owned by the engine. HumanAgent minted its own
tokens, so liveOffer() had to approximate matching with hand + street + seat
— ambiguous, because a player can face two decisions on one street (bet, get
raised, act again). Table stamps one monotonic token per decision, carried on
both DecisionContext and TableSnapshot.toActToken, so liveOffer() matches
exactly and the residual race is gone rather than narrowed.
4. Presentation logic moved out of the composable into buttonsFor(), a pure
function of the offer, so it is testable without a Compose runtime. The app
module had no tests at all; it now has 13 covering raise visibility, fold
suppression, labels, and every liveOffer() matching case.
Each new test was verified to fail with its fix reverted: reverting raise
visibility and token matching failed exactly four, and no others.
Tests: 62 -> 141 total (64 engine JVM, 64 Android host, 13 app).
Simulation unchanged, chips conserved. Verified on the emulator; physical device
untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>