Running games always continue; new work is refused when there is no
capacity; abuse is rejected immediately.
- Docker: CPU/memory/pids caps per container (overridable in .env);
uvicorn message size, queue and concurrency limits
- Server abuse limits: connections total/per IP (close 1013), message
rate per connection (token bucket, close 1008 on floods), lobby
creation rate per IP
- Load monitor: event loop lag, CPU of the own cgroup quota and
throttling, with hysteresis (normal/busy/overloaded, severe lag
overloads immediately)
- Admission control: create_lobby and start_game refused while
overloaded or MAX_ACTIVE_GAMES are running; server_status message
broadcast on changes; /health reports load but stays ok
- AI bridge: timeout sent with requests, in-flight cap, circuit breaker
- AI service: bounded workers and queue ("overloaded"), drops expired
requests, degrades to the heuristic agent when busy or too slow
- Website: busy notices, Create/Start disabled when not accepting
games, slow retry when the server is full
- Tests for limiters, load levels, admission control and AI service
limits; docs updated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The round summary used to disappear after a fixed 5 seconds. It is now
derived from the game state: the previous round's result is shown while
the new round is in bidding and hides once card play starts. This also
survives a page reload.
- Drop the client-side round timer from useWizard
- On narrow screens show the summary in the page flow so it does not
cover the bid buttons
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Deal the hands from the shuffled stack and draw the trump card from it,
so the trump card is in nobody's hand and cannot be played
- A wizard or jester as trump card means no trump suit (previously a
wizard let the dealer choose); remove the trump selection phase, the
ChooseTrump action, the trump picker UI and the AI trump actions
- Completed tricks carry their round's trump card, so the table keeps
showing the matching trump card while the last trick of a round is
displayed
- Card corners are no longer rotated, so 6 and 9 cannot be confused
- Trump label explains why there is no trump (wizard, jester, last round)
- Tests: trump card never dealt, special cards give no trump, tricks keep
their trump card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sum of all bids may never equal the number of cards each player
holds in the round; the last bidder has to adjust their bid. This was an
optional "not equal" lobby setting and is now a fixed rule of the game
engine, so it applies to humans, AI players and training alike.
- Remove GameOptions.not_equal, the set_options field, the lobby toggle
and the --not-equal training flag
- Explain the rule in the lobby and show the blocked bid while bidding
- Drop the option from the AI observation encoding
- Tests check that no round ends with bids summing to the cards in hand
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Caddy forwards /wizard/api/* and /wizard/website/* with the prefix
stripped, so the server received /ws instead of /api/ws and the website
connected to /api/ws on the domain root.
- Server routes are now /ws and /health (was /api/ws, /api/health)
- Website derives the websocket URL from its own location (../api/ws),
giving /wizard/api/ws in production and /api/ws in development
- Vite dev proxy strips /api like the production proxy
- nginx back to the plain SPA config since Caddy strips the prefix
- /internal/ai rejects requests that came through the reverse proxy,
in addition to the shared secret
- Docs updated for the prefixed deployment
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>