Problem
Answer processing spans unmanaged timeouts in both QuestionCard and the game modes. A challenge can expire during the 600 ms answer delay, yet the callback captured before expiry can still record the result and grant rewards. Rapid navigation/restart can also leave 400/800 ms callbacks alive, allowing stale sessions to mutate the new session or update an unmounted component.
Proposed scope
- Model question/session lifecycle explicitly (
idle, answering, feedback, complete).
- Make result recording idempotent per question/session.
- Cancel all pending timers on question change, restart, expiry, and unmount.
- At commit time, verify the current session token and deadline rather than relying on stale React closures.
- Decide whether an answer selected before zero but committed after zero counts, and encode that rule consistently.
Acceptance criteria
- No score, reward, progress, badge, speech, or question mutation occurs after session completion/unmount.
- One question can produce at most one result even under double clicks and timer races.
- Restarted sessions cannot receive callbacks from the previous session.
- The score screen totals remain stable after appearing.
- Fake-timer tests cover expiry during feedback, restart during feedback, navigation, and rapid input.
Problem
Answer processing spans unmanaged timeouts in both
QuestionCardand the game modes. A challenge can expire during the 600 ms answer delay, yet the callback captured before expiry can still record the result and grant rewards. Rapid navigation/restart can also leave 400/800 ms callbacks alive, allowing stale sessions to mutate the new session or update an unmounted component.Proposed scope
idle,answering,feedback,complete).Acceptance criteria