The problem I needed to solve
A Joker draw has several stages: transfer, a possible pair, shuffle and turn progression. Accepting unrelated requests at each stage could separate model and presentation.
Implementation in this snapshot
JokerGame.TryDraw performs the logical transfer and creates one pendingMove. New moves are rejected until CompleteMove finishes that exact move; matched cards must actually have left the receiver’s hand.
The design decision and its cost
The runner detaches and transfers the same view, presents a pair and shuffles only the receiver. The sequence follows the accepted result rather than animation guesses.
Current boundaries
pendingMove is not a server transaction or automatic rollback. Stopping and restarting need coroutine, tween and orphan-view cleanup; the runner’s version token matters for obsolete sequences.
Review scenarios and contract meaning
An accepted move is more than an immediate card transfer. It may create a pair in the receiving hand, remove that pair, change the hand order and then advance the turn. pendingMove keeps these stages attached to one action. A second touch during presentation should not let the model accept another action against a partially presented state. CompleteMove also requires the paired cards to have actually left the receiver’s hand: showing removal without changing the model is insufficient to finish. This boundary keeps the logical result and table presentation connected.
Transferring the same detached view preserves card identity through presentation. The runner’s version token matters when an older sequence is still awaiting continuation after a stop or restart; that sequence must not alter the new game. This is still not automatic rollback or a database transaction. Cancellation must clean up tweens, coroutines and unowned views. Relevant scenarios include stopping during transfer, stopping during pair removal and restarting. A move lock does not establish every cancellation path is correct; those paths are an independent part of flow quality.
Reporting should also distinguish move acceptance from presentation completion. A touch requests an action, TryDraw establishes model acceptance, and animation completion is another signal. Combining them can misinterpret interrupted presentation as rule rejection. pendingMove motivates this diagnostic principle without proving the project contains dedicated reporting for every stage.
What this stage taught me
For me, a transaction here means one accepted action with a clear beginning and end. Animation gets time to run without a second tap executing gameplay midway through its presentation.




