Remove the "Reveal Votes" button since auto-reveal handles this. Add
poker:sync event so PokerRoom fetches current vote state on mount,
fixing checkmarks not appearing for other users on subsequent rounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
revealIfComplete and forceReveal now accept a Set of room member keys
and only count votes from current members. Stale votes from users who
left the room are stripped before computing averages. emitSessionState
also filters votedUserKeys and revealed votes to members only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
poker:leave was firing on every issue advance (PokerRoom unmount), causing
a race condition where participants were briefly removed and revealIfComplete
triggered after just 1 vote. Now poker:leave only emits when the user truly
leaves the room (Room component unmounts).
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>
Some Atlassian account IDs use the format '70121:uuid' which contains
a colon — an invalid character in NATS KV keys (mapped to subjects).
Sanitize by replacing colons with underscores in the OAuth KV key.
No migration needed: affected users never had stored entries since
the PUT always failed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: estimate field used || 0 which collapsed both null
(never estimated) and 0 (estimated as zero) into the same value.
The filter !estimate then matched both, so saving 0 points left
the issue in the unestimated list and advanceToNext re-pokered it.
Fix: use ?? null so 0 is a distinct valid estimate, and filter on
== null to only match truly unestimated issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Saving an estimate of 0 caused advanceToNext to re-poker the same
issue repeatedly: !0 is true and 0 === 0 matches the "unestimated"
check. Track pokered issues with an `estimated` flag so they are
skipped regardless of estimate value.
Also guard against empty session index entries in createScopedSession
(NATS KV tombstones after delete can return empty values, producing
an invalid trailing-dot key that crashes Bucket.get).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ConfigMap with grafana_dashboard label for sidecar auto-discovery,
and ServiceMonitor for kube-prometheus-stack scrape target.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pokerface is designed for desktop use. On screens below 768px, show
a simple message asking users to open on their PC or Mac. Legal
pages remain accessible on mobile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added all required Jira OAuth scopes to .env.example
- Added NATS_TOKEN and JIRA_MOCK_FALLBACK to .env.example
- Added open source section to privacy policy linking to the repo
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>
The checkAuth useEffect was overwriting the legal page view state
with 'login' when unauthenticated users navigated directly to
/terms, /privacy, or /support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused normalizeIssue and JIRA_STORY_POINTS_FIELD env var
- Add URL routing for /terms, /privacy, /support pages
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>