Participants were tracked per-session, so each new issue started with 0
participants. The first user to join+vote saw 1/1 = all voted, triggering
premature reveal. Now members are tracked on the room object and persist
across issues. revealIfComplete compares votes against room member count.
Also fixes: disconnect handler was dead code (Socket.IO v4 empties
socket.rooms before firing disconnect) — replaced with disconnecting.
Added manual "Reveal Votes" button and poker:reveal socket handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Frontend now emits poker:leave when PokerRoom unmounts, preventing
ghost participants. Also adds poker:kick socket event so any session
participant can remove a stale user — shows a small X button next to
each participant. Fixes deadlocked sessions where a disconnected user
blocks reveal (votes.size can never equal participants.size).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user closes their browser or navigates away, the socket
disconnects without emitting poker:leave. The participant stayed
in the NATS session data indefinitely. Now the disconnect handler
iterates the socket's poker rooms and calls leaveSession for each,
then broadcasts the updated state to remaining participants.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same-origin requests omit the Origin header, which was rejected in
production. Also restrict to WebSocket transport on both client and
server to eliminate need for sticky sessions with multiple replicas.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-stack app with Express/Socket.io backend, React frontend,
NATS JetStream for state, and Atlassian Jira OAuth integration.
Includes security hardening: NATS auth support, KV bucket TTL
enforcement, CAS retry for race conditions, error message
sanitization, and OAuth state stored in NATS KV.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>