Features:
- PlayerCardModal: Tap any player to view full playing card image
- OutcomeWizard: Progressive 3-step outcome selection (On Base/Out/X-Check)
- GameBoard: Expandable view showing all 9 fielder positions
- Post-roll card display: Shows batter/pitcher card based on d6 roll
- CurrentSituation: Tappable player cards with modal integration
Bug fixes:
- Fix batter not advancing after play (state_manager recovery logic)
- Add dark mode support for buttons and panels (partial - iOS issue noted)
New files:
- PlayerCardModal.vue, OutcomeWizard.vue, BottomSheet.vue
- outcomeFlow.ts constants for outcome category mapping
- TEST_PLAN_UI_OVERHAUL.md with 23/24 tests passing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- Add game_metadata to load_game_state() return dict in DatabaseOperations
- Populate team display fields (name, color, thumbnail) in _rebuild_state_from_data()
so recovered games show team colors/names
Frontend:
- Add text-outline CSS for score visibility on any background (light logos, gradients)
- Handle thumbnail 404 with @error event, show enhanced shadow when no thumbnail
- Apply consistent outline across mobile and desktop layouts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- Add game_metadata to create_game() and quick_create_game() endpoints
- Fetch team display info (lname, sname, abbrev, color, thumbnail) from
SBA API at game creation time and store in DB
- Populate GameState with team display fields from game_metadata
- Fix submit_team_lineup to cache lineup in state_manager after DB write
so auto-start correctly detects both teams ready
Frontend:
- Read team colors/names/thumbnails from gameState instead of useState
- Remove useState approach that failed across SSR navigation
- Fix create.vue redirect from legacy /games/lineup/[id] to /games/[id]
- Update game.vue header to show team names from gameState
Docs:
- Update CLAUDE.md to note dev mode has broken auth, always use prod
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Broadcast game_state_update in game_engine.start_game() so frontend
receives the active state immediately
- Add auto-start safeguard to /lineup-status endpoint for stuck pending
games (both in-memory and database code paths)
- Fix None handling in state_manager._rebuild_state_from_data() for
pending games with null inning/half values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements schedule viewing from SBA production API with week navigation
and game creation from scheduled matchups. Groups games by team matchup
horizontally with games stacked vertically for space efficiency.
Backend:
- Add schedule routes (/api/schedule/current, /api/schedule/games)
- Add SBA API client methods for schedule data
- Fix multi-worker state isolation (single worker for in-memory state)
- Add Redis migration TODO for future scalability
- Support custom team IDs in quick-create endpoint
Frontend:
- Add Schedule tab as default on home page
- Week navigation with prev/next and "Current Week" jump
- Horizontal group layout (2-6 columns responsive)
- Completed games show score + "Final" badge (no Play button)
- Incomplete games show "Play" button to create webapp game
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Bug fix: During resolution phase (dice rolling), isMyTurn was false
for both players, preventing anyone from seeing the dice roller.
Now the batting team has control during resolution since they read
their card.
Demo mode: myTeamId now returns whichever team needs to act,
allowing single-player testing of both sides.
Changes:
- Add creator_discord_id to GameState (backend + frontend types)
- Add get_current_user_optional dependency for optional auth
- Update quick-create to capture creator's discord_id
- Fix isMyTurn to give batting team control during resolution
- Demo mode: myTeamId returns active team based on phase
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When recovering game state after reconnect/refresh, the old code:
1. Only looked at the last play overall (regardless of team)
2. Used that play's batting order for the currently batting team
3. Reset the non-batting team's index to 0
This caused the wrong batter to appear after inning changes - e.g.,
batter #1 showing instead of #5 in top of 2nd after bottom of 1st ended.
Fix: Now recovers each team's batter index separately by filtering
plays by half ("top" = away team, "bottom" = home team) and finding
each team's last at-bat independently.
🤖 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>
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>
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>
Fixed state_manager._rebuild_state_from_data to provide required
current_batter field when recovering games from database. The GameState
model now requires current_batter as a LineupPlayerState object, but
recovery was not populating this field, causing validation errors.
Changes:
- state_manager.py: Create placeholder current_batter during recovery
- Build LineupPlayerState from first active batter (batting_order=1)
- Fallback to first available lineup if no #1 batter found
- Raise error if no lineups exist (invalid game state)
- _prepare_next_play() will correct the batter after recovery
- Moved get_lineup_player helper to top of method (removed duplicate)
- tests/unit/core/test_state_manager.py: Update test to use new structure
- test_update_state_nonexistent_raises_error: Create LineupPlayerState
instead of using old current_batter_lineup_id field
All 26 state_manager unit tests passing. Game recovery now works
correctly in terminal client - fixes "current_batter Field required"
validation error when running status command on recovered games.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Architectural Improvement**: Unified player references in GameState
**Changed**: Make all player references consistent
- BEFORE: current_batter/pitcher/catcher were IDs (int)
- AFTER: current_batter/pitcher/catcher are full LineupPlayerState objects
- Matches pattern of on_first/on_second/on_third (already objects)
**Benefits**:
1. Consistent API - all player references use same type
2. Self-contained GameState - everything needed for resolution
3. No lookups needed - direct access to player data
4. Sets foundation for Phase 3E-Main (adding position ratings)
**Files Modified**:
- app/models/game_models.py: Changed current_batter/pitcher/catcher to objects
- app/core/game_engine.py: Updated _prepare_next_play() to populate full objects
- app/core/state_manager.py: Create placeholder batter on game creation
- tests/unit/models/test_game_models.py: Updated all 27 GameState tests
**Database Operations**:
- No schema changes needed
- Play table still stores IDs (for referential integrity)
- IDs extracted from objects when saving: state.current_batter.lineup_id
**Testing**:
- All 27 GameState tests passing
- No regressions in existing functionality
- Type checking passes
**Next**: Phase 3E-Main - Add PositionRating dataclass and load ratings at game start
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Phase 2 refactoring to consolidate runner advancement logic:
**Flyball System Enhancement**:
- Add FLYOUT_BQ variant (medium-shallow depth)
- 4 flyball types with clear semantics: A (deep), B (medium), BQ (medium-shallow), C (shallow)
- Updated helper methods to include FLYOUT_BQ
**RunnerAdvancement Integration**:
- Extend runner_advancement.py to handle both groundballs AND flyballs
- advance_runners() routes to _advance_runners_groundball() or _advance_runners_flyball()
- Comprehensive flyball logic with proper DECIDE mechanics per flyball type
- No-op movements recorded for state recovery consistency
**PlayResolver Refactoring**:
- Consolidate all 4 flyball outcomes to delegate to RunnerAdvancement (DRY)
- Eliminate duplicate flyball resolution code
- Rename helpers for clarity: _advance_on_single_1/_advance_on_single_2 (was _advance_on_single)
- Fix single/double advancement logic for different hit types
**State Recovery Fix**:
- Fix state_manager.py game recovery to build LineupPlayerState objects properly
- Use get_lineup_player() helper to construct from lineup data
- Correctly track runners in on_first/on_second/on_third fields (matches Phase 2 model)
**Database Support**:
- Add runner tracking fields to play data for accurate recovery
- Include batter_id, on_first_id, on_second_id, on_third_id, and *_final fields
**Type Safety Improvements**:
- Fix lineup_id access throughout runner_advancement.py (was accessing on_first directly, now on_first.lineup_id)
- Make current_batter_lineup_id non-optional (always set by _prepare_next_play)
- Add type: ignore for known SQLAlchemy false positives
**Documentation**:
- Update CLAUDE.md with comprehensive flyball documentation
- Add flyball types table, usage examples, and test coverage notes
- Document differences between groundball and flyball mechanics
**Testing**:
- Add test_flyball_advancement.py with 21 flyball tests
- Coverage: all 4 types, DECIDE scenarios, no-op movements, edge cases
🚀 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Refactor: Outcome-First Architecture
- PlayResolver now accepts league_id and auto_mode in constructor
- Added core resolve_outcome() method - all resolution logic in one place
- Added resolve_manual_play() wrapper for manual submissions (primary)
- Added resolve_auto_play() wrapper for PD auto mode (rare)
- Removed SimplifiedResultChart (obsolete with new architecture)
- Removed play_resolver singleton
RunnerAdvancement Integration:
- All groundball outcomes (GROUNDBALL_A/B/C) now use RunnerAdvancement
- Proper DP probability calculation with positioning modifiers
- Hit location tracked for all relevant outcomes
- 13 result types fully integrated from advancement charts
Game State Updates:
- Added auto_mode field to GameState (stored per-game)
- Updated state_manager.create_game() to accept auto_mode parameter
- GameEngine now uses state.auto_mode to create appropriate resolver
League Configuration:
- Added supports_auto_mode() to BaseGameConfig
- SbaConfig: returns False (no digitized cards)
- PdConfig: returns True (has digitized ratings)
- PlayResolver validates auto mode support and raises error for SBA
Play Results:
- Added hit_location field to PlayResult
- Groundballs include location from RunnerAdvancement
- Flyouts track hit_location for tag-up logic (future)
- Other outcomes have hit_location=None
Testing:
- Completely rewrote test_play_resolver.py for new architecture
- 9 new tests covering initialization, strikeouts, walks, groundballs, home runs
- All 9 tests passing
- All 180 core tests still passing (1 pre-existing failure unrelated)
Terminal Client:
- No changes needed - defaults to manual mode (auto_mode=False)
- Perfect for human testing of manual submissions
This completes Week 7 Task 6 - the final task of Week 7!
Week 7 is now 100% complete with all 8 tasks done.
🎯 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit includes Week 6 player models implementation and critical
performance optimizations discovered during testing.
## Player Models (Week 6 - 50% Complete)
**New Files:**
- app/models/player_models.py (516 lines)
- BasePlayer abstract class with polymorphic interface
- SbaPlayer with API parsing factory method
- PdPlayer with batting/pitching scouting data support
- Supporting models: PdCardset, PdRarity, PdBattingCard, PdPitchingCard
- tests/unit/models/test_player_models.py (692 lines)
- 32 comprehensive unit tests, all passing
- Tests for BasePlayer, SbaPlayer, PdPlayer, polymorphism
**Architecture:**
- Simplified single-layer approach vs planned two-layer
- Factory methods handle API → Game transformation directly
- SbaPlayer.from_api_response(data) - parses SBA API inline
- PdPlayer.from_api_response(player_data, batting_data, pitching_data)
- Full Pydantic validation, type safety, and polymorphism
## Performance Optimizations
**Database Query Reduction (60% fewer queries per play):**
- Before: 5 queries per play (INSERT play, SELECT play with JOINs,
SELECT games, 2x SELECT lineups)
- After: 2 queries per play (INSERT play, UPDATE games conditionally)
Changes:
1. Lineup caching (game_engine.py:384-425)
- Check state_manager.get_lineup() cache before DB fetch
- Eliminates 2 SELECT queries per play
2. Remove unnecessary refresh (operations.py:281-302)
- Removed session.refresh(play) after INSERT
- Eliminates 1 SELECT with 3 expensive LEFT JOINs
3. Direct UPDATE statement (operations.py:109-165)
- Changed update_game_state() to use direct UPDATE
- No longer does SELECT + modify + commit
4. Conditional game state updates (game_engine.py:200-217)
- Only UPDATE games table when score/inning/status changes
- Captures state before/after and compares
- ~40-60% fewer updates (many plays don't score)
## Bug Fixes
1. Fixed outs_before tracking (game_engine.py:551)
- Was incorrectly calculating: state.outs - result.outs_recorded
- Now correctly captures: state.outs (before applying result)
- All play records now have accurate out counts
2. Fixed game recovery (state_manager.py:312-314)
- AttributeError when recovering: 'GameState' has no attribute 'runners'
- Changed to use state.get_all_runners() method
- Games can now be properly recovered from database
## Enhanced Terminal Client
**Status Display Improvements (terminal_client/display.py:75-97):**
- Added "⚠️ WAITING FOR ACTION" section when play is pending
- Shows specific guidance:
- "The defense needs to submit their decision" → Run defensive [OPTIONS]
- "The offense needs to submit their decision" → Run offensive [OPTIONS]
- "Ready to resolve play" → Run resolve
- Color-coded command hints for better UX
## Documentation Updates
**backend/CLAUDE.md:**
- Added comprehensive Player Models section (204 lines)
- Updated Current Phase status to Week 6 (~50% complete)
- Documented all optimizations and bug fixes
- Added integration examples and usage patterns
**New Files:**
- .claude/implementation/week6-status-assessment.md
- Comprehensive Week 6 progress review
- Architecture decision rationale (single-layer vs two-layer)
- Completion status and next priorities
- Updated roadmap for remaining Week 6 work
## Test Results
- Player models: 32/32 tests passing
- All existing tests continue to pass
- Performance improvements verified with terminal client
## Next Steps (Week 6 Remaining)
1. Configuration system (BaseConfig, SbaConfig, PdConfig)
2. Result charts & PD play resolution with ratings
3. API client for live roster data (deferred)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced awkward "lookback" pattern with clean "prepare → execute → save"
orchestration that captures state snapshots BEFORE each play.
Key improvements:
- Added per-team batter indices (away_team_batter_idx, home_team_batter_idx)
- Added play snapshot fields (current_batter/pitcher/catcher_lineup_id)
- Added on_base_code bit field for efficient base situation queries
- Created _prepare_next_play() method for snapshot preparation
- Refactored start_game() with hard lineup validation requirement
- Refactored resolve_play() with explicit 6-step orchestration
- Updated _save_play_to_db() to use snapshots (no DB lookbacks)
- Enhanced state recovery to rebuild from last play (single query)
- Added defensive lineup position validator
Benefits:
- No special cases for first play
- Single source of truth in GameState
- Saves 18+ database queries per game
- Fast state recovery without replay
- Complete runner tracking (before/after positions)
- Explicit orchestration (easy to debug)
Testing:
- Added 3 new test functions (lineup validation, snapshot tracking, batting order)
- All 5 test suites passing (100%)
- Type checking cleaned up with targeted suppressions for SQLAlchemy
Documentation:
- Added comprehensive "Type Checking & Common False Positives" section to CLAUDE.md
- Created type-checking-guide.md and type-checking-summary.md
- Added mypy.ini configuration for SQLAlchemy/Pydantic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>