Frontend: Full 5-phase interactive wizard for uncapped hit decisions
(lead advance, defensive throw, trail advance, throw target, safe/out)
with mobile-first design, offense/defense role switching, and auto-
clearing on workflow completion.
Backend fixes:
- Remove nested asyncio.Lock acquisition causing deadlocks in all
submit_uncapped_* methods and initiate_uncapped_hit (non-re-entrant)
- Preserve pending_manual_roll during interactive workflows
- Add league_id to all dice_system.roll_d20() calls
- Extract D20Roll.roll int for state serialization
- Fix batter-runner not advancing when non-targeted in throw
- Fix rollback_plays not recalculating scores from remaining plays
Files: 10 modified, 1 new (UncappedHitWizard.vue)
Tests: 2481/2481 passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add full multi-step decision workflow for SINGLE_UNCAPPED and DOUBLE_UNCAPPED
outcomes, replacing the previous stub that fell through to basic single/double
advancement. The decision tree follows the same interactive pattern as X-Check
resolution with 5 phases: lead runner advance, defensive throw, trail runner
advance, throw target selection, and safe/out speed check.
- game_models.py: PendingUncappedHit model, 5 new decision phases
- game_engine.py: initiate_uncapped_hit(), 5 submit methods, 3 result builders
- handlers.py: 5 new WebSocket event handlers
- ai_opponent.py: 5 AI decision stubs (conservative defaults)
- play_resolver.py: Updated TODO comments for fallback paths
- 80 new backend tests (2481 total): workflow (49), handlers (23), truth tables (8)
- Fix GameplayPanel.spec.ts: add missing Pinia setup, fix component references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix critical encoding mismatch where calculate_on_base_code() returned
bit-field encoding (3=R1+R2, 4=R3) but runner_advancement.py charts
expected sequential encoding (3=R3, 4=R1+R2). Values 3 and 4 were
swapped, causing wrong groundball results for R1+R2 and R3-only scenarios.
Add comprehensive test coverage:
- 1184 invariant tests (structural correctness across all outcomes × base codes)
- 49 hit truth table tests (SINGLE_1/2, DOUBLE_2/3, TRIPLE, HOMERUN)
- 33 walk truth table tests (WALK, HBP with stat flag verification)
- 42 simple out truth table tests (STRIKEOUT, LINEOUT, POPOUT, WP, PB)
- 88 groundball truth table tests (GB_A/B/C × infield back/in/corners_in × locations)
Total: 2401 unit tests passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runner highlights and cards:
- Pills: red-500 ring, red-50 background when selected
- Full cards: red gradient (red-900 to red-950), red-600 border
- Pulse glow: red animation (rgba(239, 68, 68))
- Hardcoded red color (#ef4444) for runner pill borders
Catcher highlights and cards:
- Pill: blue-500 ring, blue-50 background when selected
- Full card: blue gradient (blue-900 to blue-950), blue-600 border
- Pulse glow: blue animation (rgba(59, 130, 246))
Updated tests to expect new colors
All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
- When catcher pill is clicked, display lead runner (3B > 2B > 1B priority) + catcher side-by-side
- Maintains consistent layout whether runner or catcher is selected
- Add leadRunnerBase computed to find highest priority runner
- Add displayedRunnerBase computed to show selected OR lead runner
- Update template to use displayedRunnerPlayer for consistent rendering
All 15 RunnersOnBase tests passing
- Swap base order to 3B, 2B, 1B (left to right, closer to baseball diamond)
- Auto-select lead runner on mount (priority: 3B > 2B > 1B)
- Make catcher pill clickable to show catcher card only
- Add 'catcher' as a selection option alongside runner bases
- Update expanded view to handle catcher-only display (centered, single card)
- Add toggleCatcher() function
- Update tests for new base order and auto-selection behavior
All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
- Replace .border-l-4.border-gray-600 checks with .catcher-pill
- Update isExpanded prop references to isSelected
- Adjust expanded view tests for new side-by-side layout
- matchup-card-blue = runner full card
- matchup-card = catcher full card
- Fix deselection test to check isSelected prop (Transition keeps DOM during animation)
All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
Backend changes:
- Add PendingXCheck model for interactive x-check state
- Extend decision_phase/pending_decision validators with 4 new phases
- Add initiate_x_check() to roll dice and present chart to player
- Add submit_x_check_result() to process player selection
- Add resolve_x_check_from_selection() to resolve from player input
- Add WebSocket handlers for x-check workflow
- Modify resolve_manual_play() to route X_CHECK to interactive flow
- All 986 unit tests passing
Frontend changes:
- Extend DecisionPhase type with x-check/DECIDE phases
- Add XCheckData, DecideAdvanceData, DecideThrowData, DecideSpeedCheckData interfaces
- Add PendingXCheck to GameState
- Add 4 new client→server WebSocket events
Next: Implement XCheckWizard component and GameplayPanel integration
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Critical bug fix for issue where Groundball A with runner on first would
fail to execute a double play after game recovery from database.
Root cause: current_on_base_code field was not recalculated during state
recovery, defaulting to 0 (empty bases) even when runners were on base.
This caused runner advancement logic to select Result 1 (batter out,
runners hold) instead of Result 2 (double play).
Changes:
- Added calculate_on_base_code() helper method to GameState model
- Updated _prepare_next_play() to use helper (eliminates duplication)
- Fixed state recovery to calculate current_on_base_code from runners
- Fixed X-Check G1 mapping (was GROUNDBALL_B, should be GROUNDBALL_A)
- Added 5 regression tests to prevent recurrence
Testing:
- All 359 unit tests passing
- New regression tests verify fix and demonstrate bug scenario
- Tested in network dev environment - double plays now work correctly
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Test empty/occupied/expanded states
- Test player name handling and initials
- Test runner selection and catcher card expansion
- Test team color integration
- All 40 tests passing
- Removed GameBoard component from both mobile and desktop layouts
- Removed unused GameBoard import
- RunnersOnBase component now handles runner display
- Cleaner UI focused on pitcher/batter matchup and expandable runner cards
- Created RunnersOnBase.vue component with split layout (runners left, catcher right)
- Created RunnerCard.vue for individual runner cards with expand-in-place functionality
- Integrated component into GamePlay.vue (mobile and desktop layouts)
- Added team color computed properties for batting/fielding teams
- Component only shows when runners on base (hasRunners computed)
- Click runner card to expand in place and show full player card + catcher matchup
- Smooth CSS transitions and animations matching pitcher/batter card style
- Includes design documentation and HTML mockup for reference
- Add ./dev-native.sh start --network for network-accessible development
- Auto-detects network IP and configures all URLs accordingly
- Create .env.network templates for backend and frontend
- No manual environment variable editing needed
- Shows Discord OAuth redirect URI to add
Usage:
./dev-native.sh start # localhost only
./dev-native.sh start --network # accessible from network
./dev-native.sh restart --network # restart in network mode
Benefits:
- Test from phone/tablet/laptop on same network
- One command to switch between local and network modes
- Automatic IP detection and configuration
- Add dev-native.sh script for fast local development (10s vs 3-5min Docker builds)
- Fix cookie security flags to respect APP_ENV (dev uses Secure=false, prod uses Secure=true)
- Pin backend to Python 3.13 (3.14 not yet supported by pydantic-core)
- Add comprehensive NATIVE_DEV_SETUP.md documentation
- Update CLAUDE.md to recommend native dev workflow
- Add .pids/ and .logs/ to .gitignore for native dev artifacts
Benefits:
- Instant startup (5-10 seconds)
- Hot-reload enabled (backend + frontend)
- Native debugging support
- No Docker rebuilds needed
- Discord OAuth works on localhost with proper cookie settings
- Add validation in create_game() and quick_create_game() to ensure both
teams are successfully fetched from SBA API before creating game
- Raise HTTP 400 with clear error message if team data cannot be fetched
- Add warning logs in get_teams_by_ids() when teams are missing from result
- Prevents games from being created with null team display info (names,
abbreviations, colors, thumbnails)
Root cause: get_teams_by_ids() silently returned empty dict on API failures,
and game creation endpoints didn't validate the result.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- Add home_team_dice_color and away_team_dice_color to GameState model
- Extract dice_color from game metadata in StateManager (default: cc0000)
- Add runners_on_base param to roll_ab for chaos check skipping
Frontend - Dice Display:
- Create DiceShapes.vue with SVG d6 (square) and d20 (hexagon) shapes
- Apply home team's dice_color to d6 dice, white for resolution d20
- Show chaos d20 in amber only when WP/PB check triggered
- Add automatic text contrast based on color luminance
- Reduce blank space and remove info bubble from dice results
Frontend - Player Cards:
- Consolidate pitcher/batter cards to single location below diamond
- Add active card highlighting based on dice roll (d6_one: 1-3=batter, 4-6=pitcher)
- New card header format: [Team] Position [Name] with full card image
- Remove redundant card displays from GameBoard and GameplayPanel
- Enlarge PlayerCardModal on desktop (max-w-3xl at 1024px+)
Tests:
- Add DiceShapes.spec.ts with 34 tests for color calculations and rendering
- Update DiceRoller.spec.ts for new DiceShapes integration
- Fix test_roll_dice_success for new runners_on_base parameter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- Add vuedraggable for mobile-friendly lineup building
- Add touch delay and threshold settings for better mobile UX
- Add drag ghost/chosen/dragging visual states
- Add replacement mode visual feedback when dragging over occupied slots
- Add getBench action to useGameActions for substitution panel
- Add BN (bench) to valid positions in LineupPlayerState
- Update lineup service to load full lineup (active + bench)
- Add touch-manipulation CSS to UI components (ActionButton, ButtonGroup, ToggleSwitch)
- Add select-none to prevent text selection during touch interactions
- Add mobile touch patterns documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add player_positions JSONB column to roster_links (migration 006)
- Add player_data JSONB column to cache name/image/headshot (migration 007)
- Add is_pitcher/is_batter computed properties for two-way player support
- Update lineup submission to populate RosterLink with all players + positions
- Update get_bench handler to use cached data (no runtime API calls)
- Add BenchPlayer type to frontend with proper filtering
- Add new Lineup components: InlineSubstitutionPanel, LineupSlotRow,
PositionSelector, UnifiedLineupTab
- Add integration tests for get_bench_players
Bench players now load instantly without API dependency, and properly
filter batters vs pitchers (including CP closer position).
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>
- ScoreBoard: Dynamic gradient using team colors (away left, home right)
with dark center blend and 20% overlay for text readability
- Fetch team colors from API using cached season from schedule state
- Fix sticky tabs by removing overflow-auto from game layout main
- Move play-by-play below gameplay panel on mobile layout
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use useWebSocket composable directly as source of truth for connection
status instead of gameStore.isConnected which could get out of sync.
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>
- Refactor game page into tab container with Game, Lineups, Stats tabs
- Extract GamePlay, LineupBuilder, GameStats components from page
- Add manager-aware default tab logic (pending + manager → Lineups tab)
- Implement per-team lineup submission (each team submits independently)
- Add lineup-status endpoint with actual lineup data for recovery
- Fix lineup persistence across tab switches and page refreshes
- Query database directly for pending games (avoids GameState validation)
- Add userTeamIds to auth store for manager detection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed ACCESS_TOKEN_MAX_AGE from 1 hour to 24 hours to reduce
frequency of re-authentication prompts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Lineup builder: Use layout: false to remove white border/padding
- Create game: Swap team order so Away Team appears above Home Team
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Bug fixes:
- Fix pitcher filter to recognize SP, RP, CP positions (not just P)
- Fix validation to allow 9 players when pitcher bats (no DH games)
- Simplify position filters to All/Batters/Pitchers
Image display improvements:
- Use headshot > vanity_card priority (avoid card images in circles)
- Show player initials as fallback (e.g., "AV" for Alex Verdugo)
- Handle name suffixes (Jr, Sr, II, III, IV) correctly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Features:
- Player search box with filter by name
- Position filter tabs (All, Catchers, Infielders, Outfielders, Pitchers)
- Player preview modal on click (shows positions, wara rating)
- Clear lineup button with confirmation styling
- Progress bar showing lineup completion
- Player headshots in both roster list and lineup slots
- Skeleton loading state during data fetch
- Sticky navigation header with back button
- Improved visual styling throughout (pills, cards, badges)
TypeScript fixes:
- Added pitcherPlayer computed property for proper type narrowing
- Removed non-existent SbaPlayer stats (batting_average, home_runs, rbi)
- Fixed unused variable warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documents deployment strategy using Docker Compose on existing Linode
infrastructure. Includes architecture diagram, step-by-step deployment
guide, NPM configuration, maintenance commands, and rollback procedures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enables precise tracking of which webapp games correspond to specific
scheduled matchups from SBA/PD league systems.
Backend:
- Add schedule_game_id column to games table with index
- Create Alembic migration for the new column
- Update QuickCreateRequest to accept schedule_game_id
- Update GameListItem response to include schedule_game_id
- Update DatabaseOperations.create_game() to store the link
Frontend:
- Pass schedule_game_id when creating game from "Play" button
- Add activeScheduleGameMap to track webapp games by schedule ID
- Show "In Progress" (green) link for active games
- Show "Resume" (green) link for pending games
- Show "Play" (blue) button for unstarted games
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>
Added HIGH-003 task documenting the isMyTurn resolution phase bug
that was discovered and fixed during integration testing. All 6
production blockers now complete.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>