The phone owned the session. The games owned the round. Neither was allowed to forget what the other had just said.
The handoff
A round started in the app, ran inside one of three games, and came back as a score. If that message landed late, or twice, the player saw a result that never happened.
The game can crash. The score cannot.
What crossed
The message
One shape, both sides. The app sent it. The game answered with the same id.
type Round = {
id: string
game: 'one' | 'two' | 'three'
instruction: string
}
type Result = {
id: string
score: number
}
Who talked
That was the whole contract. Three games, one conversation.