Commit Graph

96 Commits

Author SHA1 Message Date
Cal Corum
4cb8e3f6c4 CLAUDE: Auto-start game when both lineups submitted
- 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>
2026-01-16 14:56:35 -06:00
Cal Corum
1f5e290d8b CLAUDE: Add game page tabs with lineup persistence and per-team submission
- 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>
2026-01-16 14:08:39 -06:00
Cal Corum
5562d8de36 CLAUDE: Extend access token expiration to 24 hours
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>
2026-01-16 09:57:34 -06:00
Cal Corum
a22513b053 CLAUDE: Add schedule_game_id linking for webapp games to external schedules
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>
2026-01-14 23:55:53 -06:00
Cal Corum
fbbb1cc5da CLAUDE: Add SBA schedule integration with weekly matchup display
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>
2026-01-14 23:39:31 -06:00
Cal Corum
38fb76c849 CLAUDE: Fix resolution phase control and add demo mode
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>
2026-01-13 23:47:21 -06:00
Cal Corum
c5869f5ba2 CLAUDE: Add team ownership to auth flow (CRIT-002)
Backend:
- Add get_teams_by_owner() to SBA API client
- Update /api/auth/me to return user's teams from SBA API
- Add sba_current_season config setting (default: 13)

Frontend:
- Replace hardcoded myTeamId with computed from auth store teams
- Fix isMyTurn logic to check actual team ownership
- Update REFACTORING_PLAN.json

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 20:28:08 -06:00
Cal Corum
e0c12467b0 CLAUDE: Improve UX with single-click OAuth, enhanced games list, and layout fix
Frontend UX improvements:
- Single-click Discord OAuth from home page (no intermediate /auth page)
- Auto-redirect authenticated users from home to /games
- Fixed Nuxt layout system - app.vue now wraps NuxtPage with NuxtLayout
- Games page now has proper card container with shadow/border styling
- Layout header includes working logout with API cookie clearing

Games list enhancements:
- Display team names (lname) instead of just team IDs
- Show current score for each team
- Show inning indicator (Top/Bot X) for active games
- Responsive header with wrapped buttons on mobile

Backend improvements:
- Added team caching to SbaApiClient (1-hour TTL)
- Enhanced GameListItem with team names, scores, inning data
- Games endpoint now enriches response with SBA API team data

Docker optimizations:
- Optimized Dockerfile using --chown flag on COPY (faster than chown -R)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:14:00 -06:00
Cal Corum
46cf1cc02b CLAUDE: Fix outs_before capturing AFTER value instead of BEFORE
The outs_before field in play records was incorrectly storing the
outs count AFTER _apply_play_result() modified state.outs, rather
than the value before the play resolved.

Fix: Capture state.outs before calling _apply_play_result() and
pass it explicitly to _save_play_to_db() as a required parameter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 09:46:46 -06:00
Cal Corum
dc9f98ad88 CLAUDE: Fix batting order recovery after inning changes
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>
2025-11-28 23:38:54 -06:00
Cal Corum
4d2c905a1c CLAUDE: Fix RunnerAdvancementData tuple unpacking regression
After changing runners_advanced from list[tuple] to list[RunnerAdvancementData],
three locations were still trying to unpack as tuples causing "cannot unpack
non-iterable RunnerAdvancementData object" error on triples/doubles with runners.

Fixed locations:
- app/core/game_engine.py:772 - _apply_play_result() advancement map
- app/core/game_engine.py:1081 - _save_play() finals lookup
- terminal_client/display.py:150 - Runner movement display

Also renamed refactor_overview.md to game-engine-play-tracking-design.md
for clarity and updated the example code to use dataclass access pattern.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:11:26 -06:00
Cal Corum
450ef830dc CLAUDE: Fix batter not advancing after plays
Root cause: WebSocket handler had stale state reference after
resolve_manual_play() completed. The handler's state object still had
the old batter index, and calling state_manager.update_state()
overwrote the game engine's updated state.

Fix: Re-fetch state from state_manager AFTER resolve_manual_play()
returns, ensuring we get the state with the advanced batter index.

Also improved logging in _prepare_next_play() to show batting_order
for easier debugging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 22:38:10 -06:00
Cal Corum
acd080b437 CLAUDE: Fix Safari/iPad auth failure on game detail page
Root cause: Auth middleware was commented out on game detail page
([id].vue), causing SSR to render without checking authentication.
Safari's client-side auth check wasn't reaching the backend due to
caching behavior, resulting in "Auth: Failed" display.

Changes:
- Re-enabled middleware: ['auth'] in pages/games/[id].vue
- Added /api/auth/ws-token endpoint for Safari WebSocket fallback
- Added expires_minutes param to create_token() for short-lived tokens
- Added token query param support to WebSocket handlers
- Updated SAFARI_WEBSOCKET_ISSUE.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:53:20 -06:00
Cal Corum
19b35f148b CLAUDE: Load play history on mid-game join via game_state_sync
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>
2025-11-28 12:38:56 -06:00
Cal Corum
9c90893b5d CLAUDE: Update documentation across codebase
Updated CLAUDE.md files with:
- Current test counts and status
- Session injection pattern documentation
- New module references and architecture notes
- Updated Phase status (3E-Final complete)
- Enhanced troubleshooting guides

Files updated:
- Root CLAUDE.md: Project overview and phase status
- backend/CLAUDE.md: Backend overview with test counts
- backend/README.md: Quick start and development guide
- backend/app/api/CLAUDE.md: API routes documentation
- backend/app/database/CLAUDE.md: Session injection docs
- backend/app/utils/CLAUDE.md: Utilities documentation
- backend/tests/CLAUDE.md: Testing patterns and policy
- frontend-sba/CLAUDE.md: Frontend overview
- frontend-sba/store/CLAUDE.md: Store patterns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 12:10:10 -06:00
Cal Corum
9d0d29ef18 CLAUDE: Add Alembic migrations and database session injection
Database Infrastructure:
- Added Alembic migration system (alembic.ini, env.py)
- Migration 001: Initial schema
- Migration 004: Stat materialized views (enhanced)
- Migration 005: Composite indexes for performance
- operations.py: Session injection support for test isolation
- session.py: Enhanced session management

Application Updates:
- main.py: Integration with new database infrastructure
- health.py: Enhanced health checks with pool monitoring

Integration Tests:
- conftest.py: Session injection pattern for reliable tests
- test_operations.py: Database operations tests
- test_migrations.py: Migration verification tests

Session injection pattern enables:
- Production: Auto-commit per operation
- Testing: Shared session with automatic rollback
- Transactions: Multiple ops, single commit

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 12:09:09 -06:00
Cal Corum
4253b71db9 CLAUDE: Enhance WebSocket handlers with comprehensive test coverage
WebSocket Infrastructure:
- Connection manager: Improved connection/disconnection handling
- Handlers: Enhanced event handlers for game operations

Test Coverage (148 new tests):
- test_connection_handlers.py: Connection lifecycle tests
- test_connection_manager.py: Manager operations tests
- test_handler_locking.py: Concurrency/locking tests
- test_query_handlers.py: Game query handler tests
- test_rate_limiting.py: Rate limit enforcement tests
- test_substitution_handlers.py: Player substitution tests
- test_manual_outcome_handlers.py: Manual outcome workflow tests
- conftest.py: Shared WebSocket test fixtures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 12:08:43 -06:00
Cal Corum
3623ad6978 CLAUDE: Add game state eviction and resource management
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>
2025-11-28 12:07:55 -06:00
Cal Corum
2a392b87f8 CLAUDE: Add rate limiting, pool monitoring, and exception infrastructure
- 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>
2025-11-28 12:06:10 -06:00
Cal Corum
ae4a92f0e0 CLAUDE: Fix Safari/iPad WebSocket connection issues
- 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>
2025-11-28 12:03:57 -06:00
Cal Corum
b12905a71b CLAUDE: Add HBP (Hit By Pitch) handler to PlayResolver
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>
2025-11-27 22:53:41 -06:00
Cal Corum
920d1c599c CLAUDE: Add Undo Last Play feature for game rollback
- 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>
2025-11-27 21:34:48 -06:00
Cal Corum
9f88317b79 CLAUDE: Fix cookie security and SSR data fetching for iPad/Safari
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>
2025-11-27 21:06:42 -06:00
Cal Corum
dfc7ac99af CLAUDE: Fix runner advancement logic for doubles and hit location
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>
2025-11-26 22:21:51 -06:00
Cal Corum
e90a907e9e CLAUDE: Implement server-side OAuth flow with HttpOnly cookies
Fixes iPad Safari authentication issue where async JavaScript is blocked
on OAuth callback pages after cross-origin redirects (Cloudflare + Safari ITP).

**Problem**: iPad Safari blocks all async operations (Promises, setTimeout,
onMounted) on the OAuth callback page, preventing frontend token exchange.

**Solution**: Move entire OAuth flow to backend with HttpOnly cookies,
eliminating JavaScript dependency on callback page.

## Backend Changes (7 files)

### New Files
- app/services/oauth_state.py - Redis-based OAuth state management
  * CSRF protection with one-time use tokens (10min TTL)
  * Replaces frontend sessionStorage state validation

- app/utils/cookies.py - HttpOnly cookie utilities
  * Access token: 1 hour, Path=/api
  * Refresh token: 7 days, Path=/api/auth
  * Security: HttpOnly, Secure (prod), SameSite=Lax

### Modified Files
- app/api/routes/auth.py
  * NEW: GET /discord/login - Initiate OAuth with state creation
  * NEW: GET /discord/callback/server - Server-side callback handler
  * NEW: POST /logout - Clear auth cookies
  * UPDATED: GET /me - Cookie + header support (backwards compatible)
  * UPDATED: POST /refresh - Cookie + body support (backwards compatible)
  * FIXED: exchange_code_for_token() accepts redirect_uri parameter

- app/config.py
  * Added discord_server_redirect_uri config
  * Added frontend_url config for post-auth redirects

- app/websocket/handlers.py
  * Updated connect handler to parse cookies from environ
  * Falls back to auth object for backwards compatibility

- .env.example
  * Added DISCORD_SERVER_REDIRECT_URI example
  * Added FRONTEND_URL example

## Frontend Changes (10 files)

### Core Auth Changes
- store/auth.ts - Complete rewrite for cookie-based auth
  * Removed: token, refreshToken, tokenExpiresAt state (HttpOnly)
  * Added: checkAuth() - calls /api/auth/me with credentials
  * Updated: loginWithDiscord() - redirects to backend endpoint
  * Updated: logout() - calls backend logout endpoint
  * All $fetch calls use credentials: 'include'

- pages/auth/callback.vue - Simplified to error handler
  * No JavaScript token exchange needed
  * Displays errors from query params
  * Verifies auth with checkAuth() on success

- plugins/auth.client.ts
  * Changed from localStorage init to checkAuth() call
  * Async plugin to ensure auth state before navigation

- middleware/auth.ts - Simplified
  * Removed token validity checks (HttpOnly cookies)
  * Simple isAuthenticated check

### Cleanup Changes
- composables/useWebSocket.ts
  * Added withCredentials: true
  * Removed auth object with token
  * Updated canConnect to use isAuthenticated only

- layouts/default.vue, layouts/game.vue, pages/index.vue, pages/games/[id].vue
  * Removed initializeAuth() calls (handled by plugin)

## Documentation
- OAUTH_IPAD_ISSUE.md - Problem analysis and investigation notes
- OAUTH_SERVER_SIDE_IMPLEMENTATION.md - Complete implementation guide
  * Security improvements summary
  * Discord Developer Portal setup instructions
  * Testing checklist
  * OAuth flow diagram

## Security Improvements
- Tokens stored in HttpOnly cookies (XSS-safe)
- OAuth state in Redis with one-time use (CSRF-safe)
- Follows OAuth 2.0 Security Best Current Practice
- Backwards compatible with Authorization header auth

## Testing
-  Backend OAuth endpoints functional
-  Token exchange with correct redirect_uri
-  Cookie-based auth working
-  WebSocket connection with cookies
-  Desktop browser flow verified
-  iPad Safari testing pending Discord redirect URI config

## Next Steps
1. Add Discord redirect URI in Developer Portal:
   https://gameplay-demo.manticorum.com/api/auth/discord/callback/server
2. Test complete flow on iPad Safari
3. Verify WebSocket auto-reconnection with cookies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 22:16:30 -06:00
Cal Corum
9627a79dce CLAUDE: Add decision_required WebSocket event and quick-create testing endpoint
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>
2025-11-21 15:40:27 -06:00
Cal Corum
bcbf6036c7 CLAUDE: Fix state recovery batter advancement and flyball descriptions
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>
2025-11-21 15:38:29 -06:00
Cal Corum
a87d149788 CLAUDE: Implement game creation and lineup submission workflow
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>
2025-11-20 23:57:03 -06:00
Cal Corum
9b30d3dfb2 CLAUDE: Implement Discord OAuth authentication and SBA API integration
## 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>
2025-11-20 16:54:27 -06:00
Cal Corum
a4b99ee53e CLAUDE: Replace black and flake8 with ruff for formatting and linting
Migrated to ruff for faster, modern code formatting and linting:

Configuration changes:
- pyproject.toml: Added ruff 0.8.6, removed black/flake8
- Configured ruff with black-compatible formatting (88 chars)
- Enabled comprehensive linting rules (pycodestyle, pyflakes, isort,
  pyupgrade, bugbear, comprehensions, simplify, return)
- Updated CLAUDE.md: Changed code quality commands to use ruff

Code improvements (490 auto-fixes):
- Modernized type hints: List[T] → list[T], Dict[K,V] → dict[K,V],
  Optional[T] → T | None
- Sorted all imports (isort integration)
- Removed unused imports
- Fixed whitespace issues
- Reformatted 38 files for consistency

Bug fixes:
- app/core/play_resolver.py: Fixed type hint bug (any → Any)
- tests/unit/core/test_runner_advancement.py: Removed obsolete random mock

Testing:
- All 739 unit tests passing (100%)
- No regressions introduced

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 15:33:21 -06:00
Cal Corum
2521833afb CLAUDE: Add configurable regulation_innings and outs_per_inning
Issue #8 from code review - hardcoded inning limit (9) and outs (3)
prevented custom game modes like 7-inning doubleheaders.

Changes:
- Added regulation_innings: int = 9 to GameState (default standard)
- Added outs_per_inning: int = 3 to GameState (default standard)
- Updated validators.py: is_game_over(), can_continue_inning(), shallow outfield
- Updated game_models.py: is_game_over() uses state.regulation_innings
- Updated game_engine.py: _finalize_play() uses state.outs_per_inning

Now supports custom game modes:
- 7-inning doubleheaders
- 6-inning youth/minor league games
- Alternative out rules (2-out innings, etc.)

All 739 unit tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 20:06:57 -06:00
Cal Corum
b95c5837b0 CLAUDE: Extract common resolution logic to _finalize_play method
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>
2025-11-19 16:44:04 -06:00
Cal Corum
72a3b94ce7 CLAUDE: Add transaction handling for multi-step DB operations
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>
2025-11-19 16:39:01 -06:00
Cal Corum
6f0fe24701 CLAUDE: Add test game creation utilities
- 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>
2025-11-19 16:30:29 -06:00
Cal Corum
1a562a75d2 CLAUDE: Fix pitcher/catcher recovery and lineup data format
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>
2025-11-19 16:30:05 -06:00
Cal Corum
9546d2a370 CLAUDE: Extract database schema to reference document
- 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>
2025-11-19 16:27:27 -06:00
Cal Corum
88a5207c2c CLAUDE: Refactor backend CLAUDE.md files for conciseness
Major reduction in CLAUDE.md file sizes to follow concise documentation standard:

| File | Before | After | Reduction |
|------|--------|-------|-----------|
| backend/CLAUDE.md | 2,467 | 123 | 95% |
| models/CLAUDE.md | 1,586 | 102 | 94% |
| websocket/CLAUDE.md | 2,094 | 119 | 94% |
| config/CLAUDE.md | 1,017 | 126 | 88% |
| database/CLAUDE.md | 946 | 130 | 86% |
| api/CLAUDE.md | 906 | 140 | 85% |

Total: 9,016 -> 740 lines (92% reduction)

All files now under 150 lines with:
- Essential patterns and usage
- Cross-references to related docs
- Quick-start examples
- Updated timestamps

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 16:10:08 -06:00
Cal Corum
cbdd8cf903 CLAUDE: Fix critical game engine issues and refactor CLAUDE.md docs
Critical fixes in game_engine.py:
- Fix silent error swallowing in _batch_save_inning_rolls (re-raise)
- Add per-game asyncio.Lock for race condition prevention
- Add _cleanup_game_resources() for memory leak prevention
- All 739 tests passing

Documentation refactoring:
- Created CODE_REVIEW_GAME_ENGINE.md documenting 24 identified issues
- Trimmed backend/app/core/CLAUDE.md from 1371 to 143 lines
- Trimmed frontend-sba/CLAUDE.md from 696 to 110 lines
- Created focused subdirectory CLAUDE.md files:
  - frontend-sba/components/CLAUDE.md (105 lines)
  - frontend-sba/composables/CLAUDE.md (79 lines)
  - frontend-sba/store/CLAUDE.md (116 lines)
  - frontend-sba/types/CLAUDE.md (95 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 16:05:26 -06:00
Cal Corum
b15f80310b CLAUDE: Add LineupService and SBA API client for player data integration
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>
2025-11-19 11:55:18 -06:00
Cal Corum
b0d79ef7ef CLAUDE: Fix squeeze_bunt validation - remove bases loaded restriction
Removed the incorrect restriction that squeeze bunt cannot be used with bases
loaded. The only requirements for squeeze bunt are:
- Runner on third base
- Not with 2 outs

Updated validator and test to reflect correct rule.

Test results: 739/739 passing (100%)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 15:09:03 -06:00
Cal Corum
e165b449f5 CLAUDE: Refactor offensive decisions - replace approach with action field
Backend refactor complete - removed all deprecated parameters and replaced
with clean action-based system.

Changes:
- OffensiveDecision model: Added 'action' field (6 choices), removed
  deprecated 'hit_and_run' and 'bunt_attempt' boolean fields
- Validators: Added action-specific validation (squeeze_bunt, check_jump,
  sac_bunt, hit_and_run situational constraints)
- WebSocket handler: Updated submit_offensive_decision to use action field
- Terminal client: Updated CLI, REPL, arg parser, and display for actions
- Tests: Updated all 739 unit tests (100% passing)

Action field values:
- swing_away (default)
- steal (requires steal_attempts parameter)
- check_jump (requires runner on base)
- hit_and_run (requires runner on base)
- sac_bunt (cannot use with 2 outs)
- squeeze_bunt (requires R3, not with bases loaded, not with 2 outs)

Breaking changes:
- Removed: hit_and_run boolean → use action="hit_and_run"
- Removed: bunt_attempt boolean → use action="sac_bunt" or "squeeze_bunt"
- Removed: approach field → use action field

Files modified:
- app/models/game_models.py
- app/core/validators.py
- app/websocket/handlers.py
- terminal_client/main.py
- terminal_client/arg_parser.py
- terminal_client/commands.py
- terminal_client/repl.py
- terminal_client/display.py
- tests/unit/models/test_game_models.py
- tests/unit/core/test_validators.py
- tests/unit/terminal_client/test_arg_parser.py
- tests/unit/terminal_client/test_commands.py

Test results: 739/739 passing (100%)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 15:07:54 -06:00
Cal Corum
63bffbc23d CLAUDE: Session 1 cleanup complete - Parts 4-6
Completed remaining Session 1 work:

Part 4: Remove offensive approach field
- Removed `approach` field from OffensiveDecision model
- Removed approach validation and validator
- Updated 7 backend files (model, tests, handlers, AI, validators, display)

Part 5: Server-side depth validation
- Added walk-off validation for shallow outfield (home batting, 9th+, close game, runners)
- Updated outfield depths from ["in", "normal"] to ["normal", "shallow"]
- Infield validation already complete (corners_in/infield_in require R3)
- Added comprehensive test coverage

Part 6: Client-side smart filtering
- Updated DefensiveSetup.vue with dynamic option filtering
- Infield options: only show infield_in/corners_in when R3 present
- Outfield options: only show shallow in walk-off scenarios
- Hybrid validation (server authority + client UX)

Total Session 1: 25 files modified across 6 parts
- Removed unused config fields
- Fixed hit location requirements
- Removed alignment/approach fields
- Added complete depth validation

All backend tests passing (730/731 - 1 pre-existing failure)

Next: Session 2 - Offensive decision workflow refactor (Changes #10-11)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 13:54:34 -06:00
Cal Corum
197d91edfb CLAUDE: Remove defensive alignment field completely
Removed the unused alignment field from DefensiveDecision model and all
related code across backend and frontend.

Backend changes:
- models/game_models.py: Removed alignment field and validator
- terminal_client/display.py: Removed alignment from display
- core/ai_opponent.py: Updated log message
- tests/unit/models/test_game_models.py: Removed alignment tests
- tests/unit/core/test_validators.py: Removed alignment validation test

Frontend changes:
- types/game.ts: Removed alignment from DefensiveDecision interface
- components/Decisions/DefensiveSetup.vue:
  * Removed alignment section from template
  * Removed alignment from localSetup initialization
  * Removed alignmentOptions array
  * Removed alignmentDisplay computed property
  * Removed alignment from hasChanges comparison
  * Removed alignment from visual preview (reorganized to col-span-2)

Rationale: Defensive alignment is not active in the game and will not be
used. Per Cal's decision, remove completely rather than keep as dead code.

Tests: All 728 backend unit tests passing (100%)

Session 1 Part 3 - Change #6 complete
Part of cleanup work from demo review

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 13:02:22 -06:00
Cal Corum
eab61ad966 CLAUDE: Phases 3.5, F1-F5 Complete - Statistics & Frontend Components
This commit captures work from multiple sessions building the statistics
system and frontend component library.

Backend - Phase 3.5: Statistics System
- Box score statistics with materialized views
- Play stat calculator for real-time updates
- Stat view refresher service
- Alembic migration for materialized views
- Test coverage: 41 new tests (all passing)

Frontend - Phase F1: Foundation
- Composables: useGameState, useGameActions, useWebSocket
- Type definitions and interfaces
- Store setup with Pinia

Frontend - Phase F2: Game Display
- ScoreBoard, GameBoard, CurrentSituation, PlayByPlay components
- Demo page at /demo

Frontend - Phase F3: Decision Inputs
- DefensiveSetup, OffensiveApproach, StolenBaseInputs components
- DecisionPanel orchestration
- Demo page at /demo-decisions
- Test coverage: 213 tests passing

Frontend - Phase F4: Dice & Manual Outcome
- DiceRoller component
- ManualOutcomeEntry with validation
- PlayResult display
- GameplayPanel orchestration
- Demo page at /demo-gameplay
- Test coverage: 119 tests passing

Frontend - Phase F5: Substitutions
- PinchHitterSelector, DefensiveReplacementSelector, PitchingChangeSelector
- SubstitutionPanel with tab navigation
- Demo page at /demo-substitutions
- Test coverage: 114 tests passing

Documentation:
- PHASE_3_5_HANDOFF.md - Statistics system handoff
- PHASE_F2_COMPLETE.md - Game display completion
- Frontend phase planning docs
- NEXT_SESSION.md updated for Phase F6

Configuration:
- Package updates (Nuxt 4 fixes)
- Tailwind config enhancements
- Game store updates

Test Status:
- Backend: 731/731 passing (100%)
- Frontend: 446/446 passing (100%)
- Total: 1,177 tests passing

Next Phase: F6 - Integration (wire all components into game page)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 09:52:30 -06:00
Cal Corum
4cadb6566c CLAUDE: Clean up stale TODO comments from Phase 3E completion
Removed outdated TODO comments and updated documentation to reflect
work completed in Phase 3E (Position Ratings and WebSocket Handlers).

Changes:
- Removed 2 stale WebSocket emission TODOs in game_engine.py (lines 319, 387)
  These referenced Phase 3E-Final work completed on 2025-01-10
- Updated backend/CLAUDE.md Phase 3E status section
  Marked defender retrieval as COMPLETE (Phase 3E-Main)
  Clarified SPD test still pending (needs batter speed rating)
  Marked runner advancement as COMPLETE (Phase 3D)
- Updated TODO_RESOLUTION_SUMMARY.md
  Marked defender lookup TODO as resolved with implementation details

Documentation:
- Created TODO_AUDIT_2025-01-14.md - Complete TODO audit (53 items)
- Created TODO_VERIFICATION_RESULTS.md - Verification of resolved items
- Created TODO_SUMMARY.md - Quick reference priority matrix
- Created TODO_CLEANUP_COMPLETE.md - Cleanup work summary

Test Status:
- Backend: 9/9 PlayResolver tests passing
- No regressions introduced

Remaining Work:
- 41 legitimate TODOs properly categorized for future phases
- 8 Phase F6 TODOs (game page integration)
- 5 Quick win TODOs (optional polish)
- 28 Future phase TODOs (auth, AI, advanced features)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 08:12:08 -06:00
Cal Corum
0ebe72c09d CLAUDE: Phase 3F - Substitution System Testing Complete
This commit completes all Phase 3 work with comprehensive test coverage:

Test Coverage:
- 31 unit tests for SubstitutionRules (all validation paths)
- 10 integration tests for SubstitutionManager (DB + state sync)
- 679 total tests in test suite (609/609 unit tests passing - 100%)

Testing Scope:
- Pinch hitter validation and execution
- Defensive replacement validation and execution
- Pitching change validation and execution (min batters, force changes)
- Double switch validation
- Multiple substitutions in sequence
- Batting order preservation
- Database persistence verification
- State sync verification
- Lineup cache updates

All substitution system components are now production-ready:
 Core validation logic (SubstitutionRules)
 Orchestration layer (SubstitutionManager)
 Database operations
 WebSocket event handlers
 Comprehensive test coverage
 Complete documentation

Phase 3 Overall: 100% Complete
- Phase 3A-D (X-Check Core): 100%
- Phase 3E (Position Ratings + Redis): 100%
- Phase 3F (Substitutions): 100%

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 15:25:53 -06:00
Cal Corum
efd38d2580 CLAUDE: Phase 3F - Substitution System Testing Complete
Completed final 20% of substitution system with comprehensive test coverage.
All 640 unit tests passing (100%). Phase 3 now 100% complete.

## Unit Tests (31 tests) - NEW

tests/unit/core/test_substitution_rules.py:
- TestPinchHitterValidation: 6 tests
  * Success case
  * NOT_CURRENT_BATTER validation
  * PLAYER_ALREADY_OUT validation
  * NOT_IN_ROSTER validation
  * ALREADY_ACTIVE validation
  * Bench player edge case

- TestDefensiveReplacementValidation: 9 tests
  * Success case
  * Position change allowed
  * PLAYER_ALREADY_OUT validation
  * NOT_IN_ROSTER validation
  * ALREADY_ACTIVE validation
  * INVALID_POSITION validation
  * All valid positions (P, C, 1B-3B, SS, LF-RF, DH)
  * Mid-inning warning logged
  * allow_mid_inning flag works

- TestPitchingChangeValidation: 7 tests
  * Success case (after min batters)
  * PLAYER_ALREADY_OUT validation
  * NOT_A_PITCHER validation
  * MIN_BATTERS_NOT_MET validation
  * force_change bypasses min batters
  * NOT_IN_ROSTER validation
  * ALREADY_ACTIVE validation

- TestDoubleSwitchValidation: 6 tests
  * Success case with batting order swap
  * First substitution invalid
  * Second substitution invalid
  * INVALID_BATTING_ORDER validation
  * DUPLICATE_BATTING_ORDER validation
  * All valid batting order combinations (1-9)

- TestValidationResultDataclass: 3 tests
  * Valid result creation
  * Invalid result with error
  * Result with message only

## Integration Tests (10 tests) - NEW

tests/integration/test_substitution_manager.py:
- TestPinchHitIntegration: 2 tests
  * Full flow: validation → DB → state sync
  * Validation failure (ALREADY_ACTIVE)

- TestDefensiveReplacementIntegration: 2 tests
  * Full flow with DB/state verification
  * Position change (SS → 2B)

- TestPitchingChangeIntegration: 3 tests
  * Full flow with current_pitcher update
  * MIN_BATTERS_NOT_MET validation
  * force_change emergency bypass

- TestSubstitutionStateSync: 3 tests
  * Multiple substitutions stay synced
  * Batting order preserved after substitution
  * State cache matches database

Fixtures:
- game_with_lineups: Creates game with 9 active + 3 bench players
- Proper async session management
- Database cleanup handled

## Bug Fixes

app/core/substitution_rules.py:
- Fixed to use new GameState structure
- Changed: state.current_batter_lineup_id → state.current_batter.lineup_id
- Aligns with Phase 3E GameState refactoring

## Test Results

Unit Tests:
- 640/640 passing (100%)
- 31 new substitution tests
- All edge cases covered
- Execution: 1.02s

Integration Tests:
- 10 tests implemented
- Full DB + state sync verification
- Note: Run individually due to known asyncpg connection issues

## Documentation Updates

.claude/implementation/NEXT_SESSION.md:
- Updated Phase 3 progress to 100% complete
- Marked Task 2 (unit tests) completed
- Marked Task 3 (integration tests) completed
- Updated success criteria with completion notes
- Documented test counts and coverage

## Phase 3 Status: 100% COMPLETE 

- Phase 3A-D (X-Check Core): 100%
- Phase 3E-Prep (GameState Refactor): 100%
- Phase 3E-Main (Position Ratings): 100%
- Phase 3E-Final (Redis/WebSocket): 100%
- Phase 3E Testing (Terminal Client): 100%
- Phase 3F (Substitutions): 100%

All core gameplay features implemented and fully tested.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 22:34:17 -06:00
Cal Corum
e147ab17f1 CLAUDE: Phase 3F - Substitution System WebSocket Events
Implemented complete WebSocket integration for real-time player substitutions.
System is now 80% complete - only tests remain.

## WebSocket Events Implemented (600 lines)

### Event Handlers (backend/app/websocket/handlers.py):
1. request_pinch_hitter - Pinch hitter substitution
   - Validates: game_id, player_out_lineup_id, player_in_card_id, team_id
   - Executes: SubstitutionManager.pinch_hit()
   - Broadcasts: player_substituted (all clients), substitution_confirmed (requester)
   - Error codes: MISSING_FIELD, INVALID_FORMAT, NOT_CURRENT_BATTER, etc.

2. request_defensive_replacement - Defensive replacement
   - Additional field: new_position (P, C, 1B, 2B, 3B, SS, LF, CF, RF)
   - Executes: SubstitutionManager.defensive_replace()
   - Same broadcast pattern as pinch hitter

3. request_pitching_change - Pitching change
   - Validates minimum batters faced (handled in SubstitutionManager)
   - Executes: SubstitutionManager.change_pitcher()
   - Broadcasts new pitcher to all clients

4. get_lineup - Get active lineup for team
   - Returns: lineup_data with all active players
   - Uses: StateManager cache (O(1)) or database fallback
   - Purpose: UI refresh after substitutions

### Event Pattern (follows existing handlers):
- Validate inputs (UUID format, required fields, game exists)
- Create SubstitutionManager instance with DatabaseOperations
- Execute substitution (validate → DB → state)
- Broadcast player_substituted to game room
- Send substitution_confirmed to requester
- Error handling with specific error codes

### Events Emitted:
- player_substituted (broadcast) - Includes: type, lineup IDs, position, batting_order
- substitution_confirmed (requester) - Success confirmation with new_lineup_id
- substitution_error (requester) - Validation error with error code
- lineup_data (requester) - Active lineup response
- error (requester) - Generic error

## Documentation Updates (350 lines)

### backend/app/websocket/CLAUDE.md:
- Complete handler documentation with examples
- Event data structures and response formats
- Error code reference (MISSING_FIELD, INVALID_FORMAT, NOT_CURRENT_BATTER, etc.)
- Client integration examples (JavaScript)
- Complete workflow diagrams
- Updated event summary table (+8 events)
- Updated Common Imports section

### .claude/implementation/ updates:
- NEXT_SESSION.md: Marked Task 1 complete, updated to 80% done
- SUBSTITUTION_SYSTEM_SUMMARY.md: Added WebSocket section, updated status
- GAMESTATE_REFACTOR_PLAN.md: Marked complete
- PHASE_3_OVERVIEW.md: Updated all phases to reflect completion
- phase-3e-COMPLETED.md: Created comprehensive completion doc

## Architecture

### DB-First Pattern (maintained):
```
Client Request → WebSocket Handler
    ↓
SubstitutionManager
    ├─ SubstitutionRules.validate_*()
    ├─ DatabaseOperations.create_substitution() (DB first!)
    ├─ StateManager.update_lineup_cache()
    └─ Update GameState if applicable
    ↓
Success Responses
    ├─ player_substituted (broadcast to room)
    └─ substitution_confirmed (to requester)
```

### Error Handling:
- Three-tier: ValidationError, GameValidationError, Exception
- Specific error codes for each failure type
- User-friendly error messages
- Comprehensive logging at each step

## Status Update

**Phase 3F Substitution System**: 80% Complete
-  Core logic (SubstitutionRules, SubstitutionManager) - 1,027 lines
-  Database operations (create_substitution, get_eligible_substitutes)
-  WebSocket events (4 handlers) - 600 lines
-  Documentation (350 lines)
-  Unit tests (20% remaining) - ~300 lines needed
-  Integration tests - ~400 lines needed

**Phase 3 Overall**: ~97% Complete
- Phase 3A-D (X-Check Core): 100%
- Phase 3E (GameState, Ratings, Redis, Testing): 100%
- Phase 3F (Substitutions): 80%

## Files Modified

backend/app/websocket/handlers.py (+600 lines)
backend/app/websocket/CLAUDE.md (+350 lines)
.claude/implementation/NEXT_SESSION.md (updated progress)
.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md (added WebSocket section)
.claude/implementation/GAMESTATE_REFACTOR_PLAN.md (marked complete)
.claude/implementation/PHASE_3_OVERVIEW.md (updated all phases)
.claude/implementation/phase-3e-COMPLETED.md (new file, 400+ lines)

## Next Steps

Remaining work (2-3 hours):
1. Unit tests for SubstitutionRules (~300 lines)
   - 15+ pinch hitter tests
   - 12+ defensive replacement tests
   - 10+ pitching change tests
2. Integration tests for SubstitutionManager (~400 lines)
   - Full DB + state sync flow
   - State recovery verification
   - Error handling and rollback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 21:24:43 -06:00
Cal Corum
beb939b32a CLAUDE: Fix all unit test failures and implement 100% test requirement
Test Fixes (609/609 passing):
- Fixed DiceSystem API to accept team_id/player_id parameters for audit trails
- Fixed dice roll history timing issue in test
- Fixed terminal client mock to match resolve_play signature (X-Check params)
- Fixed result chart test mocks with missing pitching fields
- Fixed flaky test by using groundball_a (exists in both batting/pitching)

Documentation Updates:
- Added Testing Policy section to backend/CLAUDE.md
- Added Testing Policy section to tests/CLAUDE.md
- Documented 100% unit test requirement before commits
- Added git hook setup instructions

Git Hook System:
- Created .git-hooks/pre-commit script (enforces 100% test pass)
- Created .git-hooks/install-hooks.sh (easy installation)
- Created .git-hooks/README.md (hook documentation)
- Hook automatically runs all unit tests before each commit
- Blocks commits if any test fails

All 609 unit tests now passing (100%)
Integration tests have known asyncpg connection issues (documented)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 19:35:21 -06:00
Cal Corum
c7b376df4f CLAUDE: Update documentation for GameState refactoring and X-Check testing
Updated CLAUDE.md files to document recent changes including the GameState
refactoring and new X-Check testing capabilities in the terminal client.

Changes to app/models/CLAUDE.md:
- Updated GameState field documentation
  - Replaced current_batter_lineup_id references with current_batter
  - Documented LineupPlayerState requirement for current players
  - Added comprehensive usage examples

- Added "Recent Updates" section documenting GameState refactoring
  - Before/after code examples showing migration path
  - Explanation of why the change was made
  - Migration notes for developers
  - List of all affected files (7 files updated)

Changes to terminal_client/CLAUDE.md:
- Added "2025-11-04: X-Check Testing & GameState Refactoring" section
  - New feature: resolve_with x-check <position> command
    - Complete X-Check resolution with defense tables and error charts
    - Shows all resolution steps with audit trail
    - Works with actual player ratings from PD API

  - Documented 8 X-Check commands now in help system
    - roll_jump / test_jump, roll_fielding / test_fielding
    - test_location, rollback, force_wild_pitch, force_passed_ball

  - Bug fixes documented
    - GameState structure updates (display.py, repl.py)
    - Game recovery fix (state_manager.py)
    - DO3 advancement fix (play_resolver.py)

  - Complete testing workflow examples
  - List of 7 files updated
  - Test coverage status (all passing)

- Updated footer: Last Updated 2025-11-04

These documentation updates provide clear migration guides for the GameState
refactoring and comprehensive examples for the new X-Check testing features.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 16:09:58 -06:00