The droplet copy of this file had drifted from the repo across
multiple sessions. This commit brings the repo in line with what
is actually running in production.
Changes landing from the droplet:
- Add restart: unless-stopped to app, postgres, and redis services
(the deploy.restart_policy block was swarm-only and silently
ignored by compose v2; top-level restart: is the correct form)
- Drop the dead deploy.replicas: 1 and deploy.restart_policy:
subfields from the app service (swarm-only noise)
- Remove the traefik: service block entirely; Traefik now runs as
its own stack at /opt/traefik from the adlee-traefik repo
- Remove the letsencrypt: volume declaration (owned by adlee-traefik
now via the external golfgame_letsencrypt volume)
- Change web network from "driver: bridge" to
"name: traefik_web, external: true" so this stack attaches to the
shared ingress network created by the traefik stack
This closes INC-0001/AI-10 and unblocks future deploys of this repo.
The other uncommitted client/*, tui_client/* changes in the working
tree are unrelated and intentionally left alone.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the previous MIT license with GPL-3.0-or-later. Adds the full
GPL-3.0 license text at LICENSE, updates pyproject.toml metadata and
classifier, updates the README, and adds SPDX-License-Identifier headers
to all first-party server Python and client JavaScript sources.
Third-party anime.min.js is left untouched.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused card_revealed broadcast + 1s asyncio.sleep in swap handler
(client never handled this message, causing pure dead wait before game_state)
- Defer swap-out (opacity:0) on hand cards to onStart callback so overlay
covers the card before hiding it — eliminates visual gap for all players
- Defer heldCardFloating visibility hide to onStart — held card stays visible
until animation overlay replaces it
- Thread onStart callback through animateUnifiedSwap → _runUnifiedSwap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Fix IndexError in current_player() when player leaves mid-game
- remove_player() now adjusts current_player_index after popping
- current_player() has safety bounds check as defensive fallback
2. Fix AssertionError in StaticFiles catching WebSocket upgrades
- Wrap static file mount to reject non-HTTP requests gracefully
- Starlette's StaticFiles asserts scope["type"] == "http"
Both crashes were observed in production on 2026-02-28 during a
multi-player session. The IndexError cascaded into reconnection
attempts that hit the StaticFiles assertion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reveal face-down cards briefly (1s) before swap completes, using
client-side state diffing instead of a separate server message.
Local player reveals use existing card data; opponent reveals use
server-sent card_revealed as a fallback. Defers incoming game_state
updates during the reveal window to prevent overwrites.
Also update YOUR TURN badge to cyan with suit symbols.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-codebase commenting pass focused on the tricky, fragile, and
non-obvious spots: animation coordination flags in app.js, AI decision
safety checks in ai.py, scoring evaluation order in game.py, animation
engine magic numbers in card-animations.js, and server infrastructure
coupling in main.py/handlers.py/room.py. No logic changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dark green felt background for game screen
- Status bar: dark brown bg with amber text instead of blue
- YOUR TURN badge: green bg with white text instead of bright gold
- 3s delay before hole-complete scoreboard overlay
- Dealer indicator changed from Ⓓ to (D)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace [esc][esc] quit with [q] quit globally (immediate on login,
confirmation prompt elsewhere)
- [esc] is now consistently "back": signup→login, lobby→log out (with
confirm), in-room host→leave (with confirm), in-room guest→leave
- Extract ConfirmScreen to shared screens/confirm.py
- Move dealer Ⓓ indicator to bottom-left corner of player box border
- Scoreboard now tags OUT (went out first) and ⭐ (lowest score)
- Send finisher_id and player id in round_over server message
- Room code moved inside in-room section with amber border
- Lobby title uses branded ⛳🏌️ GolfCards.club ♠♥♣♦
- Amber borders and dark green backgrounds on login/lobby containers
- Deck preview renders actual card-back shapes (▓▒▓/▒▓▒)
- Help/standings panels close only with [esc], hint updated
- Game footer: s[⇥]andings [h]elp on left, [q]uit on right
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Save JWT token to ~/.config/golfcards/session.json after login so
subsequent launches skip the login screen when the session is still
valid. A new splash screen shows the token check status (SUCCESS /
NONE FOUND / EXPIRED) before routing to lobby or login.
Also: move OUT indicator to player box bottom border, remove checkmark,
center scoreboard overlay, use alternating shade blocks (▓▒▓/▒▓▒) for
card backs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Periodic room cleanup now updates games_v2 status to 'abandoned'
- Server startup marks all orphaned active games as abandoned
- Prevents stale games from accumulating in the admin portal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rooms that sit idle (no player actions or CPU turns) for longer than
ROOM_IDLE_TIMEOUT_SECONDS (default 300s) are now automatically cleaned
up: CPU tasks cancelled, players notified with room_expired, WebSockets
closed, and room removed from memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Single Esc: goes back one step (signup→login, lobby→connect, room→lobby)
- Double Esc: still quits the app
- Footer bar shows [Esc] Back and [Esc][Esc] Quit hints on all screens
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add signup with invite code support, remove guest login
- Add quit confirmation (q), help screen (h), standings tab
- Unified footer: [h]elp [q]uit | action text | [tab] standings
- Amber card highlighting persists through entire initial flip phase
- Player box border only highlights on turn (green) or knock (red)
- Play area gold border only during player's actual turn
- Game end returns to lobby create/join instead of login screen
- Lobby reset_to_pre_room for replayability without reconnecting
- Dynamic opponent layout fits all in one row when terminal is wide enough
- Hole emoji (⛳) in status bar, branded title with suits on connect screen
- DECK label spacing, Hole terminology in scoreboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lobby: collapsible Game Settings, House Rules, Deck Style sections
- Lobby: CPU profile picker via [+], random CPU via [?], remove via [-]
- Lobby: all settings (rounds, decks, flip mode, house rules, deck colors)
sent to server on start_game instead of hardcoded defaults
- Game: clickable cards (hand positions, deck, discard pile)
- Game: immediate visual feedback on initial card flips
- Game: action bar shows escaped keyboard hints (Keyboard: Choose [d]eck...)
- Game: play area uses fixed-width rounded box instead of horizontal lines
- Game: position numbers on card top-left corner (replacing ┌) on all states
- Game: deck color preview swatches next to style dropdown
- Fix opponent box height mismatch when match connectors present
- Rebrand to GolfCards.club
- Add spacing between status bar/opponents and above local hand
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables public beta signup metering: DAILY_OPEN_SIGNUPS env var controls
how many users can register without an invite code per day (0=disabled,
-1=unlimited, N=daily cap). Invite codes always bypass the limit.
Also adds per-IP signup throttling (DAILY_SIGNUPS_PER_IP, default 3/day)
and fail-closed rate limiting on auth endpoints when Redis is down.
Client dynamically fetches /api/auth/signup-info to show invite field
as optional with remaining slots when open signups are enabled.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update version across pyproject.toml, FastAPI app, HTML footers,
and V3.17 doc. Mark kicked-ball bug as resolved in docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Base .golfer-container rule was after the mobile @media override,
clobbering it. Moved base rule before the media query. Landscape
gets -2px (snug), mobile gets 12px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce golfer-container margin from 10px to 4px in landscape (2-row)
mode while keeping 10px on mobile. Swap bottom suits to checkerboard
pattern: club/diamond on top, heart/spade on bottom.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap golfer+ball in a positioned container so the ball is absolutely
anchored to the golfer's front foot, independent of inline flow/viewport.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rearrange golf ball SVG suits from single row to 2x2 grid
- Add even spacing between logo, golfer, and title in mobile view
- Remove row-gap between logo row and title row in landscape view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 749px media query was triggering at mid-range widths, collapsing
the two-row logo+title into a single line. Fix by:
- Using inline-grid on h1 for bulletproof two-row layout
- Lowering single-line breakpoint from 749px to 500px
- Widening lobby container to 550px for title to fit naturally
- Constraining game controls to 400px max-width
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change .logo-row from inline-block to block so the golf ball logo
always left-aligns flush with the G, regardless of viewport width.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The h1 shrinks to its widest child (GolfCards.club), centers via
margin auto, and text-align left aligns both lines within it.
No breakpoint-dependent transforms needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logo and title naturally left-align within the centered lobby box.
Mobile (<480px) gets text-align center + inline display for single line.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>