Backend changes:
- Modified request_game_state handler to fetch plays from database
- Convert Play DB models to frontend-compatible PlayResult dicts
- Emit game_state_sync event with state + recent_plays array
Frontend changes:
- Added deduplication by play_number in addPlayToHistory()
- Prevents duplicate plays when game_state_sync is received
Field mapping from Play model:
- hit_type -> outcome
- result_description -> description
- batter_id -> batter_lineup_id
- batter_final -> batter_result
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced PlayByPlay with a tabbed interface:
- "Recent" tab shows plays from current half inning only
- "Scoring" tab shows all plays where runs were scored
- Badge counts on each tab show number of matching plays
- Tab-aware empty states with contextual messaging
- Footer shows total game plays count
Removed unused showFilters prop and showAllPlays toggle.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Lineouts are simple outs with no runner advancement logic - the backend
doesn't use hit_location for them (unlike flyouts for sac flies or
groundballs for double plays). Updated OUTCOMES_REQUIRING_HIT_LOCATION
to exclude lineout and added documentation explaining the rationale.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
New Tests:
- test_exceptions.py: Comprehensive tests for custom GameEngineError
hierarchy including AuthorizationError, DatabaseError, etc.
Scripts:
- stop-services.sh: Enhanced to kill entire process trees and
clean up orphan processes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auth Improvements:
- auth.ts: Enhanced authentication store with better error handling
- auth.ts middleware: Improved redirect logic
- login.vue: Better login flow and error display
Game Display:
- PlayByPlay.vue: Enhanced play-by-play feed with player name display
- game.ts types: Additional type definitions for runner advancement
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Game Engine Improvements:
- State manager: Add game eviction with configurable max games, idle
timeout, and memory limits
- Game engine: Add resource cleanup on game completion
- Play resolver: Enhanced RunnerAdvancementData with lineup_id for
player name resolution in play-by-play
- Substitution manager: Minor improvements
Test Coverage:
- New test_game_eviction.py with 13 tests for eviction scenarios
- Updated state_manager tests for new functionality
- Updated play_resolver tests for lineup_id handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add rate_limit.py middleware with per-client throttling and cleanup task
- Add pool_monitor.py for database connection pool health monitoring
- Add custom exceptions module (GameEngineError, DatabaseError, etc.)
- Add config settings for eviction intervals, session timeouts, memory limits
- Add unit tests for rate limiting and pool monitoring
🤖 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>
Fixed missing PlayOutcome.HIT_BY_PITCH handling in resolve_outcome().
HBP was being submitted via WebSocket but rejected with error:
"Unhandled outcome: PlayOutcome.HIT_BY_PITCH"
Changes:
- Added HBP case to play_resolver.py (lines 430-446)
- HBP uses same forced runner advancement logic as WALK
- HBP is NOT classified as is_walk=True (correct for stats)
- Added 2 unit tests: test_hit_by_pitch, test_hit_by_pitch_bases_loaded
Test results: 981/981 passing (100%)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move 1st and 3rd base slightly inward for better diagonal alignment
- Adjust vertical positioning for proper edge alignment with 2nd base
- Mobile: top-[64%], Desktop: top-[60%] for optimal display per breakpoint
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Restructured mobile layout to match desktop:
- Runners+Outs column now beside Inning box in same row
- Layout: Away | [Inning][Runners/Outs] | Home
- Consistent layout between mobile and desktop views
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove Count box (not needed for this game mode)
- Stack Runners above Outs in compact vertical column
- Fix diamond orientation: bases now at corners (matching GameBoard)
- Change base markers from circles to diamond squares
- Remove home plate, keep only 1st/2nd/3rd bases
- Remove diamond outline for cleaner look
- Remove Outs label, keep only indicator circles
- Tighten spacing to align with Inning box height
- Remove unused balls/strikes props
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added OUTFIELD_ONLY_OUTCOMES constant for flyout_a, flyout_b, flyout_c
- Added showInfieldLocations computed to hide infield for flyballs
- Updated selectOutcome to clear infield location when switching to flyball
- Mirrors existing groundball logic that hides outfield positions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added rollback_play WebSocket handler (handlers.py:1632)
- Accepts game_id and num_plays (default: 1)
- Validates game state and play count
- Broadcasts play_rolled_back and game_state_update events
- Full error handling with rate limiting
- Added undoLastPlay action to useGameActions composable
- Emits rollback_play event to backend
- Added Undo button to game page ([id].vue)
- Amber floating action button with undo arrow icon
- Positioned above substitutions button
- Only visible when game is active and has plays
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Bug fix:
- Fixed hasRunners prop using wrong property path (gameState.runners.first
instead of gameState.on_first) - hit location selector was never showing
Optimization:
- Hide outfield positions (LF, CF, RF) for groundball outcomes since
groundballs by definition stay in the infield
- Auto-clear outfield selection when switching to groundball outcome
🤖 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>
Added helper scripts and documentation for improved developer experience:
1. ACCESS_CONTROL.md
- Comprehensive Discord whitelist documentation
- Configuration examples and security best practices
- Troubleshooting guide for common access issues
- Details both OAuth and test token protection points
2. start-services.sh
- One-command startup for backend + frontend
- Logs to logs/ directory with PIDs tracked
- Displays URLs and helpful information
- 3-second backend initialization delay
3. stop-services.sh
- Clean shutdown with process tree killing
- Removes orphaned processes by pattern matching
- Graceful TERM followed by KILL if needed
- Cleans up PID files
These utilities streamline local development by:
- Reducing manual startup steps
- Ensuring clean shutdown (no orphaned processes)
- Providing clear access control guidance
Scripts are now executable and ready to use:
./start-services.sh
./stop-services.sh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added a "Quick Start Demo" button to the games list page that allows users
to instantly create and jump into a pre-configured demo game without going
through the full game creation flow.
Features:
- Green button prominently placed next to "Create New Game"
- Loading state with disabled button and "Creating..." text
- Calls /api/games/quick-create endpoint
- Auto-redirects to the created game page
- Error handling with user-friendly messages
This improves the user experience for testing and demos by reducing the
game creation flow from multiple steps to a single click.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added devServer configuration to bind on all network interfaces,
enabling access through Nginx Proxy Manager for remote testing.
Changes:
- Set host to '0.0.0.0' (listen on all interfaces)
- Keep port at 3000
This allows the frontend to be accessed via:
- Local: http://localhost:3000
- Remote: http://gameplay-demo.manticorum.com (via NPM proxy)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two related bug fixes for gameplay accuracy:
**Backend - play_resolver.py**:
- Fixed DOUBLE2 advancement: Runners now advance exactly 2 bases
* 1st → 3rd, 2nd → home, 3rd → home
* Was incorrectly advancing all runners to home
- Fixed DOUBLE3 advancement: Runners now advance exactly 3 bases
* All runners score (1st+3=4, 2nd+3=5→4, 3rd+3=6→4)
* Updated docstrings for clarity
**Frontend - ManualOutcomeEntry.vue**:
- Fixed hit location requirement logic
* Now requires hit location when runners on base (any outs)
* Was incorrectly restricting to only when outs < 2
* Hit location determines runner advancement regardless of outs
These fixes ensure accurate Strat-O-Matic gameplay simulation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed property access bug introduced in previous commit:
**Issue**:
- DefensiveSetup.vue was accessing `gameState.runners.third` and `runners.first/second/third`
- GameState type doesn't have a `runners` property
- Caused TypeScript error: "Property 'runners' does not exist on type 'GameState'"
**Fix**:
- Changed to use correct properties: `on_first`, `on_second`, `on_third`
- Updated infieldDepthOptions: `props.gameState?.on_third` (line 163)
- Updated outfieldDepthOptions: destructure `on_first, on_second, on_third` (line 178)
- Updated hasRunners check: `on_first || on_second || on_third` (line 184)
**Why**:
GameState uses individual nullable properties for runners, not a nested object.
This matches the backend Pydantic model structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Comprehensive documentation for real-time gameplay workflow:
**New Documentation**:
1. WEBSOCKET_PROTOCOL_SPEC.md (49KB)
- Complete catalog of all 15 backend WebSocket event handlers
- Complete catalog of all frontend event listeners
- Game workflow sequences (connection → game start → play resolution)
- Critical issues identified and resolution status
- Event payload specifications with examples
- Timing and performance expectations
2. DECISION_REQUIRED_IMPLEMENTATION.md (11KB)
- Issue #1 detailed analysis and resolution
- Backend implementation of decision_required event
- Frontend integration approach
- Before/After workflow comparison
- Test validation results
3. GAMEPLAY_SESSION_HANDOFF.md (10KB)
- Session work summary and accomplishments
- Live testing results and observations
- Known issues and next steps
- Quick start guide for next session
- Technical architecture notes
**Why**:
- Provides single source of truth for WebSocket protocol
- Documents complete event flow for frontend/backend alignment
- Captures implementation decisions and rationale
- Enables faster onboarding for new developers
- Creates reference for debugging WebSocket issues
**Impact**:
- Reduces protocol confusion between frontend and backend
- Accelerates future WebSocket feature development
- Provides clear integration patterns for new events
🤖 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>
Backend enhancements for real-time decision workflow:
**New Features**:
- decision_required event emission when game starts and after each decision
- Quick-create endpoint (/games/quick-create) for rapid testing with pre-configured lineups
- WebSocket connection manager integration in GameEngine
**Changes**:
- game_engine.py: Added _emit_decision_required() method and set_connection_manager()
- game_engine.py: Emit decision_required on game start with 5-minute timeout
- games.py: New /quick-create endpoint with Team 35 vs Team 38 lineups
- main.py: Wire connection manager to game_engine singleton
- state_manager.py: Enhanced state management for decision phases
- play_resolver.py: Improved play resolution logic
- handlers.py: Updated WebSocket handlers for new workflow
- backend/CLAUDE.md: Added WebSocket protocol spec reference
**Why**:
Eliminates polling - frontend now gets real-time notification when decisions are needed.
Quick-create saves 2 minutes of lineup setup during each test iteration.
**Testing**:
- Manual testing with terminal client
- WebSocket event flow verified with live frontend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes two critical bugs in the game engine and updates tests
to match current webhook behavior:
1. State Recovery - Batter Advancement (operations.py:545-546)
- Added missing batting_order and outs_recorded fields to plays dictionary
- These fields exist in database but weren't loaded by load_game_state()
- Root cause: Batter index was correctly recovered but current_batter remained
at placeholder (batting_order=1) because recovery logic couldn't find the
actual batting_order from last play
- Fix enables proper batter advancement after backend restarts
2. Flyball Descriptions - 2 Outs Logic (runner_advancement.py)
- Made flyball descriptions dynamic based on outs and actual base runners
- FLYOUT_A (Deep): Lines 1352-1363
- FLYOUT_B (Medium): Lines 1438-1449
- FLYOUT_BQ (Medium-shallow): Lines 1521-1530
- With 2 outs: "3rd out, inning over" (no advancement possible)
- With 0-1 outs: Dynamic based on runners ("R3 scores" only if R3 exists)
- Game logic was already correct (runs_scored=0), only descriptions were wrong
- Fixed method signatures to include hit_location parameter for all flyball methods
- Updated X-check function calls to pass hit_location parameter
3. Test Updates
- Fixed test expectation in test_flyball_advancement.py to match corrected behavior
- Descriptions now only show runners that actually exist (no phantom "R2 DECIDE")
- Auto-fixed import ordering with Ruff
- Updated websocket tests to match current webhook behavior:
* test_submit_manual_outcome_success now expects 2 broadcasts (play_resolved + game_state_update)
* test_submit_manual_outcome_missing_required_location updated to reflect hit_location now optional
Testing:
- All 739 unit tests passing (100%)
- Verified batter advances correctly after state recovery
- Verified flyball with 2 outs shows correct description
- Verified dynamic descriptions only mention actual runners on base
- Verified websocket handler broadcasts work correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove accidentally created backend/pages directory (Vue files belong in frontend).
Added pages/ to backend .gitignore to prevent future confusion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete implementation of pre-game setup flow allowing players to create games
and submit lineups before gameplay starts.
Backend Changes:
- Extended games.py with create game, lineup submission, and game start endpoints
- Added teams.py roster endpoint with season filtering
- Enhanced SBA API client with player data fetching and caching
- Comprehensive validation for lineup submission (position conflicts, DH rules)
Frontend Changes:
- Redesigned create.vue with improved team selection and game options
- Enhanced index.vue with active/pending game filtering and navigation
- Added lineup/[id].vue for interactive lineup builder with drag-and-drop
- Implemented auth.client.ts plugin for client-side auth initialization
- Added comprehensive TypeScript types for API contracts
- Updated middleware for better auth handling
Key Features:
- Game creation with home/away team selection
- Full lineup builder with position assignment and batting order
- DH rule validation (pitcher can be excluded from batting order)
- Season-based roster filtering (Season 3)
- Auto-start game when both lineups submitted
- Real-time game list updates
Workflow:
1. Create game → select teams → set options
2. Submit home lineup → validate positions/order
3. Submit away lineup → validate positions/order
4. Game auto-starts → navigates to game page
5. WebSocket connection → loads game state
Ready for Phase F4 - connecting gameplay UI to complete the at-bat loop.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Authentication Implementation
### Backend
- Implemented complete Discord OAuth flow in auth.py:
* POST /api/auth/discord/callback - Exchange code for tokens
* POST /api/auth/refresh - Refresh JWT tokens
* GET /api/auth/me - Get authenticated user info
* GET /api/auth/verify - Verify auth status
- JWT token creation with 7-day expiration
- Refresh token support for session persistence
- Bearer token authentication for Discord API calls
### Frontend
- Created auth/login.vue - Discord OAuth initiation page
- Created auth/callback.vue - OAuth callback handler with states
- Integrated with existing auth store (already implemented)
- LocalStorage persistence for tokens and user data
- Full error handling and loading states
### Configuration
- Updated backend .env with Discord OAuth credentials
- Updated frontend .env with Discord Client ID
- Fixed redirect URI to port 3001
## SBA API Integration
### Backend
- Extended SbaApiClient with get_teams(season, active_only=True)
- Added bearer token auth support (_get_headers method)
- Created /api/teams route with TeamResponse model
- Registered teams router in main.py
- Filters out IL (Injured List) teams automatically
- Returns team data: id, abbrev, names, color, gmid, division
### Integration
- Connected to production SBA API: https://api.sba.manticorum.com
- Bearer token authentication working
- Successfully fetches ~16 active Season 3 teams
## Documentation
- Created SESSION_NOTES.md - Current session accomplishments
- Created NEXT_SESSION.md - Game creation implementation guide
- Updated implementation/NEXT_SESSION.md
## Testing
- ✅ Discord OAuth flow tested end-to-end
- ✅ User authentication and session persistence verified
- ✅ Teams API returns real data from production
- ✅ All services running and communicating
## What Works Now
- User can sign in with Discord
- Sessions persist across reloads
- Backend fetches real teams from SBA API
- Ready for game creation implementation
## Next Steps
See .claude/NEXT_SESSION.md for detailed game creation implementation plan.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reviewed all remaining high-severity issues from code review:
Issues #6, #7 (Input validation): Already implemented in validators.py
- hold_runners validation: lines 71-77
- steal_attempts validation: lines 156-165
- Called from submit_defensive_decision and submit_offensive_decision
Issue #8 (Hardcoded inning limit): Deferred to next sprint
- Requires config system changes across validators.py and game_models.py
- Appropriate for technical debt phase
Issue #9 (Cleanup on abandon): Already fixed (part of Issue #3)
- _cleanup_game_resources() called in end_game() at line 1109
Issue #10 (Direct state mutation): Architectural acknowledgment
- Current pattern with debug logging throughout
- Consider immutable state pattern for v2 if auditability needed
Issue #11 (Logger singleton): Verified correct
- Module-level singleton at line 34, outside class
All high-priority issues now addressed or appropriately deferred.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Issue #5 from code review - resolve_play and resolve_manual_play shared
~70% of their code causing maintenance burden and divergent behavior risk.
Changes:
- Extracted common finalization logic to new _finalize_play() method
- resolve_play reduced from ~150 lines to ~60 lines
- resolve_manual_play reduced from ~135 lines to ~60 lines
- Single source of truth for: roll tracking, state capture, transaction
handling, inning advance, cleanup, and state updates
Benefits:
- Changes to play finalization only need to be made in one place
- Reduced risk of divergent behavior between resolution modes
- Better testability and maintainability
All 739 unit tests passing (1 flaky probabilistic test occasionally fails).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Issue #4 from code review - multi-step database operations were not
wrapped in transactions, risking partial state on failure.
Changes:
- Modified save_play() and update_game_state() in DatabaseOperations
to accept optional session parameter for transaction grouping
- Wrapped resolve_play() DB operations in single atomic transaction
- Wrapped resolve_manual_play() DB operations in single atomic transaction
- Transaction commits atomically or rolls back entirely on failure
Pattern: When session provided, use flush() for IDs without committing;
caller controls transaction. When no session, create internal transaction.
All 739 unit tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- test_game_data.py: Static lineup data based on real SBA Game 2519
- West Virginia Black Bears vs Columbus Hydra
- Complete lineup data with player info
- create_test_game.py: Script to end active games and create fresh test game
- Usage: uv run python scripts/create_test_game.py
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend fixes:
- state_manager: Properly recover current_pitcher and current_catcher from
fielding team during game state recovery (fixes pitcher badge not showing)
- handlers: Add headshot field to lineup data, use lineup_service for proper
player data loading on cache miss
- lineup_service: Minor adjustments for headshot support
Frontend fixes:
- player.ts: Update Lineup type to match WebSocket event format
- lineup_id (was 'id'), card_id fields
- player.headshot for UI circles
- Optional fields for event variations
- CurrentSituation.vue: Adapt to updated type structure
- Substitution selectors: Use updated Lineup type fields
This fixes the issue where pitcher badge wouldn't show after game recovery
because current_pitcher was being set from batting team instead of fielding team.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Created backend/.claude/DATABASE_SCHEMA.md with full table details
- Updated database/CLAUDE.md to reference the new schema doc
- Preserves valuable reference material while keeping CLAUDE.md concise
🤖 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>
Removed all references to the defensive alignment field across frontend codebase
after backend removal in Session 1. The alignment field was determined to be unused
and was removed from DefensiveDecision model.
Changes:
- types/websocket.ts: Removed alignment from DefensiveDecisionRequest interface
- composables/useGameActions.ts: Removed alignment from submit handler
- pages/demo-decisions.vue: Updated demo state and summary text (alignment → depths)
- pages/games/[id].vue: Updated decision history text for both defensive and offensive
* Defensive: Now shows "infield depth, outfield depth" instead of "alignment, infield"
* Offensive: Updated to use new action field with proper labels (swing_away, hit_and_run, etc.)
- Test files (3): Updated all test cases to remove alignment references
* tests/unit/composables/useGameActions.spec.ts
* tests/unit/store/game-decisions.spec.ts
* tests/unit/components/Decisions/DefensiveSetup.spec.ts
Also updated offensive decision handling to match Session 2 changes (approach/hit_and_run/bunt_attempt → action field).
Total: 7 files modified, all alignment references removed
Verified: Zero remaining alignment references in .ts/.vue/.js files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated all 4 demo pages to use new action field and added navigation footers
for easy cross-linking between demo pages.
Changes:
- demo-decisions.vue: Updated to use action field, added runner prop bindings
- demo.vue: Added footer with links to other demos
- demo-gameplay.vue: Added footer with links to other demos
- demo-substitutions.vue: Added footer with links to other demos
Demo page updates:
- OffensiveDecision now uses action field instead of approach/hit_and_run/bunt
- Action labels properly mapped (swing_away, steal, check_jump, etc.)
- Added runner state props (runnerOnFirst, runnerOnSecond, runnerOnThird)
- Added outs prop for smart filtering
Footer features:
- 3-column grid layout responsive to mobile
- Icons and descriptions for each demo
- Hover effects on links
- Consistent styling across all pages
Demo pages now fully connected:
- /demo → Game State Demo (ScoreBoard, GameBoard, etc.)
- /demo-decisions → Decision Components Demo (new action-based)
- /demo-gameplay → Gameplay Components Demo (DiceRoller, ManualOutcome)
- /demo-substitutions → Substitution Components Demo
Files modified:
- pages/demo-decisions.vue
- pages/demo.vue
- pages/demo-gameplay.vue
- pages/demo-substitutions.vue
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Frontend refactor complete - updated TypeScript interfaces and OffensiveApproach
component to use new action-based system with smart filtering.
Changes:
- TypeScript interfaces: Replaced approach/hit_and_run/bunt_attempt with action field
- OffensiveApproach.vue: Complete refactor with 6 action choices and smart filtering
- Smart filtering: Automatically disables invalid actions based on game state
- Auto-reset: If current action becomes invalid, resets to swing_away
TypeScript updates (types/game.ts, types/websocket.ts):
- OffensiveDecision.action: 6 valid choices (swing_away, steal, check_jump,
hit_and_run, sac_bunt, squeeze_bunt)
- Removed deprecated fields: approach, hit_and_run, bunt_attempt
- OffensiveDecisionRequest updated to match
Component features:
- Smart filtering based on game state (runners, outs)
- Visual feedback for disabled actions with explanatory text
- Special handling notes for steal and squeeze_bunt
- Auto-reset to swing_away when actions become invalid
- Clean, modern UI with action icons and descriptions
Action requirements enforced in UI:
- check_jump: requires runner on base
- hit_and_run: requires runner on base
- sac_bunt: disabled with 2 outs
- squeeze_bunt: requires R3, disabled with 2 outs
- steal/swing_away: always available
Files modified:
- types/game.ts
- types/websocket.ts
- components/Decisions/OffensiveApproach.vue
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>