The frontend routing guard checks has_starter_deck to decide whether to
redirect users to starter selection. The field was missing from the API
response, causing authenticated users with a starter deck to be
incorrectly redirected to /starter on page refresh.
- Add has_starter_deck computed property to User model
- Add has_starter_deck field to UserResponse schema
- Add unit tests for User model properties
- Add API tests for has_starter_deck in profile response
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ProfilePage implementation:
- Full profile page with avatar, editable display name, session count
- LinkedAccountCard and DisplayNameEditor components
- useProfile composable wrapping user store operations
- Support for linking/unlinking OAuth providers
- Logout and logout-all-devices functionality
Profanity service with bypass detection:
- Uses better-profanity library for base detection
- Enhanced to catch common bypass attempts:
- Number suffixes/prefixes (shit123, 69fuck)
- Leet-speak substitutions (sh1t, f@ck, $hit)
- Separator characters (s.h.i.t, f-u-c-k)
- Integrated into PATCH /api/users/me endpoint
- 17 unit tests covering all normalization strategies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add GameNamespaceHandler with full event handling for real-time gameplay:
- handle_join: Join/rejoin games with visibility-filtered state
- handle_action: Execute actions and broadcast state to participants
- handle_resign: Process resignation and end game
- handle_disconnect: Notify opponent of disconnection
- Broadcast helpers for state, game over, and opponent status
Includes 28 unit tests covering all handler methods.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GameEndResult dataclass with winner, loser, final views, duration
- Add _map_end_reason() to map core GameEndReason to DB EndReason
(raises ValueError for unknown reasons to catch missing enum sync)
- Enhance end_game() to build replay data and return comprehensive result
- Add archive_to_history() to GameStateManager for complete game archival:
- Creates GameHistory record with replay data
- Deletes ActiveGame record
- Clears Redis cache
- All in single transaction
- Add ArchiveResult dataclass for archive operation metadata
- Add TODO for session_factory DI refactor in GameStateManager
- Update tests: 5 new end_game tests, 6 new archive_to_history tests
Phase 4 progress: 10/18 tasks complete
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add pending forced action and game_over fields to GameJoinResult:
- pending_forced_action: Included when player must complete a forced
action (e.g., select new active after KO). Essential for reconnection
so client knows what action is required.
- game_over: Boolean indicating if game has already ended.
- is_your_turn: Now True when player has pending forced action, even if
it's technically opponent's turn.
The join_game method now handles both initial joins and reconnections
(resume). The last_event_id parameter is accepted for future event
replay support.
Tests: 4 new tests for forced action handling and game_over flag.
Total 51 tests for GameService.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix audit warnings about empty string defaults hiding data corruption:
1. get_connection_info(): Validate required fields (user_id, connected_at,
last_seen) exist before creating ConnectionInfo. Return None and log
warning for corrupted records instead of returning invalid data.
2. unregister_connection(): Log warning if user_id is missing during
cleanup. Existing code safely handles this (skips cleanup), but now
we have visibility into data corruption.
Test added for corrupted data case.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three changes to fail fast instead of silently degrading:
1. GameService.create_game: Raise GameCreationError when energy card
definition not found instead of logging warning and continuing.
A deck with missing energy cards is fundamentally broken.
2. CardService.load_all: Collect all card file load failures and raise
CardServiceLoadError at end with comprehensive error report. Prevents
startup with partial card data that causes cryptic runtime errors.
New exceptions: CardLoadError, CardServiceLoadError
3. GameStateManager.recover_active_games: Return RecoveryResult dataclass
with recovered count, failed game IDs with error messages, and total.
Enables proper monitoring and alerting for corrupted game state.
Tests added for energy card error case. Existing tests updated for
new RecoveryResult return type.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add forced action handling, turn boundary detection, and DB persistence:
- Check for pending forced actions before allowing regular actions
- Only specified player can act during forced action (except resign)
- Only specified action type allowed during forced action
- Detect turn boundaries (turn number OR current player change)
- Persist to Postgres at turn boundaries for durability
- Include pending_forced_action in GameActionResult for client
New exceptions: ForcedActionRequiredError
Tests: 11 new tests covering forced actions, turn boundaries, and
pending action reporting. Total 47 tests for GameService.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add create_game method that loads decks via DeckService, converts
CardDefinitions to CardInstances, and persists to Redis/Postgres
- Build card registry from only the cards in play (not all cards)
- Add GameCreationError exception and GameCreateResult dataclass
- Add creation_engine_factory for DI-based testing (no monkey patching)
- Add helper methods: _cards_to_instances, _build_card_registry
- Update tests with proper mocks for success, deck failure, engine failure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ConnectionManager: Add redis_factory constructor parameter
- GameService: Add engine_factory constructor parameter
- AuthHandler: New class replacing standalone functions with
token_verifier and conn_manager injection
- Update all tests to use constructor DI instead of patch()
- Update CLAUDE.md with factory injection patterns
- Update services README with new patterns
- Add socketio README documenting AuthHandler and events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WebSocket Message Schemas (WS-002):
- Add Pydantic models for all client/server WebSocket messages
- Implement discriminated unions for message type parsing
- Include JoinGame, Action, Resign, Heartbeat client messages
- Include GameState, ActionResult, Error, TurnStart server messages
Connection Manager (WS-003):
- Add Redis-backed WebSocket connection tracking
- Implement user-to-sid mapping with TTL management
- Support game room association and opponent lookup
- Add heartbeat tracking for connection health
Socket.IO Authentication (WS-004):
- Add JWT-based authentication middleware
- Support token extraction from multiple formats
- Implement session setup with ConnectionManager integration
- Add require_auth helper for event handlers
Socket.IO Server Setup (WS-001):
- Configure AsyncServer with ASGI mode
- Register /game namespace with event handlers
- Integrate with FastAPI via ASGIApp wrapper
- Configure CORS from application settings
Game Service (GS-001):
- Add stateless GameService for game lifecycle orchestration
- Create engine per-operation using rules from GameState
- Implement action-based RNG seeding for deterministic replay
- Add rng_seed field to GameState for replay support
Architecture verified:
- Core module independence (no forbidden imports)
- Config from request pattern (rules in GameState)
- Dependency injection (constructor deps, method config)
- All 1090 tests passing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UNSET sentinel pattern:
- Add UNSET sentinel in protocols.py for nullable field updates
- Fix inability to clear deck description (UNSET=keep, None=clear)
- Fix repository inability to clear validation_errors
Starter deck improvements:
- Remove unused has_starter_deck from CollectionService
- Add deprecation notes to old starter deck methods
Validation improvements:
- Add energy type validation in deck_validator.py
- Add energy type validation in deck schemas
- Add VALID_ENERGY_TYPES constant
Game loading fix:
- Fix get_deck_for_game silently skipping invalid cards
- Now raises ValueError with clear error message
Tests:
- Add TestEnergyTypeValidation test class
- Add TestGetDeckForGame test class
- Add tests for validate_energy_types utility function
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Critical fixes:
- Add admin API key authentication for admin endpoints
- Add race condition protection via unique partial index for starter decks
- Make starter deck selection atomic with combined method
Moderate fixes:
- Fix DI pattern violation in validate_deck_endpoint
- Add card ID format validation (regex pattern)
- Add card quantity validation (1-99 range)
- Fix exception chaining with from None (B904)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implemented with Repository Protocol pattern for offline fork support:
- CollectionService with PostgresCollectionRepository
- DeckService with PostgresDeckRepository
- DeckValidator with DeckConfig + CardService injection
- Starter deck definitions (5 types: grass, fire, water, psychic, lightning)
- Pydantic schemas for collection and deck APIs
- Unit tests for DeckValidator (32 tests passing)
Architecture follows pure dependency injection - no service locator patterns.
Added CLAUDE.md documenting DI requirements and patterns.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>