Refactored useWebSocket.ts to prevent SSR-related connection issues:
- Created ClientState interface to encapsulate all client-only state
- Added getClientState() function with lazy initialization on client only
- Added SSR guards (import.meta.client) to all connection functions
- Reset reactive state on client hydration to ensure clean slate
- Moved heartbeatInterval and stuckStateCheckInterval into ClientState
- Changed NodeJS.Timeout to ReturnType<typeof setTimeout/setInterval>
- Wrapped auth watcher in import.meta.client guard
Root cause: Module-level singleton state was being initialized during
SSR, then persisting in a potentially corrupted state during hydration.
This caused intermittent "Socket exists: no" issues where the browser
wouldn't even attempt WebSocket connections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Service Scripts:
- start-services.sh: Add pre-flight checks, health monitoring, --dev/--prod modes,
port options, dependency checks, and version logging
- stop-services.sh: Add port 3000 cleanup, verification, --quiet/--force flags
- status-services.sh: New script for monitoring service status with --watch mode
WebSocket:
- Remove conflicting socket.client.ts plugin that was interfering with
useWebSocket.ts composable (used JWT auth vs cookie auth)
- Add debugging logs to useWebSocket.ts to diagnose intermittent connection issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced O(n²) deduplication check per play with O(1) array replacement:
- Added setPlayHistory() for game_state_sync (replaces entire array)
- Simplified addPlayToHistory() for live play_resolved events (just push)
This separates sync operations (replace) from live events (append),
eliminating the need for deduplication checks during gameplay.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed cookie SameSite policy from Lax to None with Secure=true
for Safari ITP compatibility
- Fixed Nuxt composable context issue: moved useRuntimeConfig() from
connect() callback to composable setup phase (required in Nuxt 4)
- Added GET /logout endpoint for easy browser-based logout
- Improved loading overlay with clear status indicators and action
buttons (Retry, Re-Login, Dismiss)
- Added error handling with try-catch in WebSocket connect()
- Documented issue and fixes in .claude/SAFARI_WEBSOCKET_ISSUE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: WebSocket wouldn't connect on page load because Pinia auth
state doesn't automatically transfer from SSR to client hydration.
The auth store showed isAuthenticated=false on client mount.
Solution:
- useWebSocket.onMounted now proactively calls checkAuth() if not authenticated
- This ensures auth state is initialized before attempting WebSocket connection
- Added forceReconnect() function to clear stale singleton connections
Debug UI (temporary):
- Added connection status debug info to loading overlay and banner
- Shows Auth/Connected/Connecting/Error states
- Retry button triggers auth check + reconnect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolved WebSocket connection issues and games list loading on iPad:
- cookies.py: Added is_secure_context() to set Secure flag when accessed
via HTTPS even in development mode (Safari requires this)
- useWebSocket.ts: Changed auto-connect from immediate watcher to
onMounted hook for safer SSR hydration
- games/index.vue: Replaced onMounted + fetchGames() with useAsyncData
for SSR data fetching with proper cookie forwarding
- Updated COOKIE_AUTH_IMPLEMENTATION.md with new issues and solutions
- Updated composables/CLAUDE.md with auto-connect pattern documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Frontend alignment with backend WebSocket protocol:
**Type System Fixes**:
- types/game.ts: Changed DecisionPhase to use 'awaiting_*' convention matching backend
- types/game.ts: Fixed PlayOutcome enum values to match backend string values (e.g., 'strikeout' not 'STRIKEOUT')
- types/game.ts: Added comprehensive play outcome types (groundball_a/b/c, flyout variants, x_check)
**Decision Detection**:
- store/game.ts: Updated decision detection to check both decision prompt AND gameState.decision_phase
- components: Updated all decision phase checks to use 'awaiting_defensive', 'awaiting_offensive', 'awaiting_stolen_base'
**WebSocket Enhancements**:
- useWebSocket.ts: Added game_joined event handler with success toast
- useWebSocket.ts: Fixed dice roll data - now receives d6_two_a and d6_two_b from server
- useWebSocket.ts: Request fresh game state after decision submissions to sync decision_phase
**New Constants**:
- constants/outcomes.ts: Created centralized PlayOutcome enum with display labels and descriptions
**Testing**:
- Updated test expectations for new decision phase naming
- All component tests passing
**Why**:
Eliminates confusion from dual naming conventions. Frontend now uses same vocabulary as backend.
Fixes runtime type errors from enum value mismatches.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created centralized services for SBA player data fetching at lineup creation:
Backend - New Services:
- app/services/sba_api_client.py: REST client for SBA API (api.sba.manticorum.com)
with batch player fetching and caching support
- app/services/lineup_service.py: High-level service combining DB operations
with API calls for complete lineup entries with player data
Backend - Refactored Components:
- app/core/game_engine.py: Replaced raw API calls with LineupService,
reduced _prepare_next_play() from ~50 lines to ~15 lines
- app/core/substitution_manager.py: Updated pinch_hit(), defensive_replace(),
change_pitcher() to use lineup_service.get_sba_player_data()
- app/models/game_models.py: Added player_name/player_image to LineupPlayerState
- app/services/__init__.py: Exported new LineupService components
- app/websocket/handlers.py: Enhanced lineup state handling
Frontend - SBA League:
- components/Game/CurrentSituation.vue: Restored player images with fallback
badges (P/B letters) for both mobile and desktop layouts
- components/Game/GameBoard.vue: Enhanced game board visualization
- composables/useGameActions.ts: Updated game action handling
- composables/useWebSocket.ts: Improved WebSocket state management
- pages/games/[id].vue: Enhanced game page with better state handling
- types/game.ts: Updated type definitions
- types/websocket.ts: Added WebSocket type support
Architecture Improvement:
All SBA player data fetching now goes through LineupService:
- Lineup creation: add_sba_player_to_lineup()
- Lineup loading: load_team_lineup_with_player_data()
- Substitutions: get_sba_player_data()
All 739 unit tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>