Commit Graph

60 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
76e01420a3 CLAUDE: Fix DO3 (double-3) batter advancement
Fixed incorrect batter advancement for DO3 (double-3) outcomes. The batter
was incorrectly reaching 3B instead of 2B.

DO3 means:
- DO = Double (batter reaches 2B)
- 3 = Runners advance 3 bases

Changes:
- play_resolver.py: Fixed DOUBLE_3 outcome handling (line 416)
  - Changed batter_result from 3 to 2
  - Updated description to clarify "runners advance 3 bases" not "batter to 3rd"

- play_resolver.py: Fixed X-Check DO3 handling (line 1043)
  - Changed batter_reaches from 3 to 2
  - Added comment explaining DO3 notation

Now on a DO3 + NO error:
 Batter reaches 2B (correct)
 R1 advances 3 bases → HOME (correct)
 R2 would advance 3 bases → HOME (correct)
 R3 would advance 3 bases → HOME (correct)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 16:06:22 -06:00
Cal Corum
cf7cc23f45 CLAUDE: Fix runner_advancement for new GameState structure
Updated runner_advancement.py and its tests to use the new GameState
structure where current_batter is a LineupPlayerState object instead of
an integer ID.

Changes:
- runner_advancement.py: Replaced all 17 references to
  state.current_batter_lineup_id with state.current_batter.lineup_id

- tests/unit/core/test_runner_advancement.py: Updated test fixtures
  - Mock fixture: Added mock_batter object with lineup_id attribute
  - GameState constructor calls: Create LineupPlayerState objects instead
    of using current_batter_lineup_id parameter (9 tests fixed)

All 34 runner advancement tests now passing. This fixes the AttributeError
that was preventing X-Check resolution from completing in the terminal
client ("'GameState' object has no attribute 'current_batter_lineup_id'").

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 16:03:46 -06:00
Cal Corum
bb78de2b84 CLAUDE: Add X-Check testing to resolve_with command
Added ability to test X-Check defensive plays directly in the terminal
client using the resolve_with command. This allows testing the complete
X-Check resolution system (defense tables, error charts, runner advancement)
with actual player ratings.

Changes:
- repl.py: Updated do_resolve_with() to parse "x-check <position>" syntax
  - Accepts "x-check", "xcheck", or "x_check" followed by position
  - Validates position (P, C, 1B, 2B, 3B, SS, LF, CF, RF)
  - Passes xcheck_position parameter through to commands

- commands.py: Updated resolve_play() to accept xcheck_position parameter
  - Passes xcheck_position to game_engine.resolve_play()
  - Shows "🎯 Forcing X-Check to: <position>" message

- game_engine.py: Updated resolve_play() to accept xcheck_position parameter
  - For X_CHECK outcomes, uses xcheck_position as hit_location
  - Enables full X-Check resolution with defense tables and error charts

- help_text.py: Updated resolve_with help documentation
  - Added x-check usage syntax and examples
  - Documented position parameter requirement
  - Added note about using actual player ratings

Usage:
 > defensive
 > offensive
 > resolve_with x-check SS    # Test X-Check to shortstop
 > resolve_with x-check LF    # Test X-Check to left field

This integrates the existing play_resolver._resolve_x_check() logic,
providing a way to test the complete X-Check system including defense
range adjustments, table lookups, SPD tests, G2#/G3# conversion, error
charts, and runner advancement.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:59:16 -06:00
Cal Corum
e6bd66ee39 CLAUDE: Fix game recovery for new GameState structure
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>
2025-11-04 15:26:00 -06:00
Cal Corum
440adf2c26 CLAUDE: Update REPL for new GameState and standardize UV commands
Updated terminal client REPL to work with refactored GameState structure
where current_batter/pitcher/catcher are now LineupPlayerState objects
instead of integer IDs. Also standardized all documentation to properly
show 'uv run' prefixes for Python commands.

REPL Updates:
- terminal_client/display.py: Access lineup_id from LineupPlayerState objects
- terminal_client/repl.py: Fix typos (self.current_game → self.current_game_id)
- tests/unit/terminal_client/test_commands.py: Create proper LineupPlayerState
  objects in test fixtures (2 tests fixed, all 105 terminal client tests passing)

Documentation Updates (100+ command examples):
- CLAUDE.md: Updated pytest examples to use 'uv run' prefix
- terminal_client/CLAUDE.md: Updated ~40 command examples
- tests/CLAUDE.md: Updated all test commands (unit, integration, debugging)
- app/*/CLAUDE.md: Updated test and server startup commands (5 files)

All Python commands now consistently use 'uv run' prefix to align with
project's UV migration, improving developer experience and preventing
confusion about virtual environment activation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 09:59:13 -06:00
Cal Corum
d1619b4a1f CLAUDE: Phase 3 - Substitution System Core Logic
Implemented comprehensive substitution system with DB-first pattern:

## Core Components (1,027 lines)

1. SubstitutionRules (345 lines)
   - Validates pinch hitter, defensive replacement, pitching change
   - Enforces no re-entry, roster eligibility, active status
   - Comprehensive error messages with error codes

2. SubstitutionManager (552 lines)
   - Orchestrates DB-first pattern: validate → DB → state
   - Handles pinch_hit, defensive_replace, change_pitcher
   - Automatic state sync and lineup cache updates

3. Database Operations (+115 lines)
   - create_substitution(): Creates sub with full metadata
   - get_eligible_substitutes(): Lists inactive players

4. Model Enhancements (+15 lines)
   - Added get_player_by_card_id() to TeamLineupState

## Key Features

-  DB-first pattern (database is source of truth)
-  Immutable lineup history (audit trail)
-  Comprehensive validation (8+ rule checks)
-  State + DB sync guaranteed
-  Error handling at every step
-  Detailed logging for debugging

## Architecture Decisions

- Position flexibility (MVP - no eligibility check)
- Batting order inheritance (pinch hitter takes spot)
- No re-entry (matches real baseball rules)
- Validation uses in-memory state (fast)

## Remaining Work

- WebSocket event handlers (2-3 hours)
- Comprehensive testing (2-3 hours)
- API documentation (1 hour)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 23:50:33 -06:00
Cal Corum
02e816a57f CLAUDE: Phase 3E-Main - Position Ratings Integration for X-Check Resolution
Complete integration of position ratings system enabling X-Check defensive plays
to use actual player ratings from PD API with intelligent fallbacks for SBA.

**Live API Testing Verified**: 
- Endpoint: GET https://pd.manticorum.com/api/v2/cardpositions?player_id=8807
- Response: 200 OK, 7 positions retrieved successfully
- Cache performance: 16,601x faster (API: 0.214s, Cache: 0.000s)
- Data quality: Real defensive ratings (range 1-5, error 0-88)

**Architecture Overview**:
- League-aware: PD league fetches ratings from API, SBA uses defaults
- StateManager integration: Defenders retrieved from lineup cache
- Self-contained GameState: All data needed for X-Check in memory
- Graceful degradation: Falls back to league averages if ratings unavailable

**Files Created**:

1. app/services/pd_api_client.py (NEW)
   - PdApiClient class for PD API integration
   - Endpoint: GET /api/v2/cardpositions?player_id={id}&position={pos}
   - Async HTTP client using httpx (already in requirements.txt)
   - Optional position filtering: get_position_ratings(8807, ['SS', '2B'])
   - Returns List[PositionRating] for all positions player can play
   - Handles both list and dict response formats
   - Comprehensive error handling with logging

2. app/services/position_rating_service.py (NEW)
   - PositionRatingService with in-memory caching
   - get_ratings_for_card(card_id, league_id) - All positions
   - get_rating_for_position(card_id, position, league_id) - Specific position
   - Cache performance: >16,000x faster on hits
   - Singleton pattern: position_rating_service instance
   - TODO Phase 3E-Final: Upgrade to Redis

3. app/services/__init__.py (NEW)
   - Package exports for clean imports

4. test_pd_api_live.py (NEW)
   - Live API integration test script
   - Tests with real PD player 8807 (7 positions)
   - Verifies caching, filtering, GameState integration
   - Run: `python test_pd_api_live.py`

5. test_pd_api_mock.py (NEW)
   - Mock integration test for CI/CD
   - Demonstrates flow without API dependency

6. tests/integration/test_position_ratings_api.py (NEW)
   - Pytest integration test suite
   - Real API tests with player 8807
   - Cache verification, SBA skip logic
   - Full end-to-end GameState flow

**Files Modified**:

1. app/models/game_models.py
   - LineupPlayerState: Added position_rating field (Optional[PositionRating])
   - GameState: Added get_defender_for_position(position, state_manager)
   - Uses StateManager's lineup cache to find active defender by position
   - Iterates through lineup.players to match position + is_active

2. app/config/league_configs.py
   - SbaConfig: Added supports_position_ratings() → False
   - PdConfig: Added supports_position_ratings() → True
   - Enables league-specific behavior without hardcoded conditionals

3. app/core/play_resolver.py
   - __init__: Added state_manager parameter for X-Check defender lookup
   - _resolve_x_check(): Replaced placeholder defender ratings with actual lookup
   - Uses league config to check if ratings supported
   - Fetches defender via state.get_defender_for_position()
   - Falls back to defaults (range=3, error=15) if ratings unavailable
   - Detailed logging for debugging rating lookups

4. app/core/game_engine.py
   - Added _load_position_ratings_for_lineup() method
   - Loads all position ratings at game start for PD league
   - Skips loading for SBA (league config check)
   - start_game(): Calls rating loader for both teams before marking active
   - PlayResolver instantiation: Now passes state_manager parameter
   - Logs: "Loaded X/9 position ratings for team Y"

**X-Check Resolution Flow**:
1. League check: config.supports_position_ratings()?
2. Get defender: state.get_defender_for_position(pos, state_manager)
3. If PD + defender.position_rating exists: Use actual range/error
4. Else if defender found: Use defaults (range=3, error=15)
5. Else: Log warning, use defaults

**Position Rating Loading (Game Start)**:
1. Check if league supports ratings (PD only)
2. Get lineup from StateManager cache
3. For each player:
   - Fetch rating from position_rating_service (with caching)
   - Set player.position_rating field
4. Cache API responses (16,000x faster on subsequent access)
5. Log success: "Loaded X/9 position ratings for team Y"

**Live Test Results (Player 8807)**:
```
Position   Range    Error    Innings
CF         3        2        372
2B         3        8        212
SS         4        12       159
RF         2        2        74
LF         3        2        62
1B         4        0        46
3B         3        65       34
```

**Testing**:
-  Live API: Player 8807 → 7 positions retrieved successfully
-  Caching: 16,601x performance improvement
-  League config: SBA=False, PD=True
-  GameState integration: Defender lookup working
-  Existing tests: 27/28 config tests passing (1 pre-existing URL failure)
-  Syntax validation: All files compile successfully

**Benefits**:
-  X-Check now uses real defensive ratings in PD league
-  SBA league continues working with manual entry (uses defaults)
-  No breaking changes to existing functionality
-  Graceful degradation if API unavailable
-  In-memory caching reduces API calls by >99%
-  League-agnostic design via config system
-  Production-ready with live API verification

**Phase 3E Status**: Main complete (85% → 90%)
**Next**: Phase 3E-Final (WebSocket events, Redis upgrade, full defensive lineup)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 21:00:37 -06:00
Cal Corum
d560844704 CLAUDE: Phase 3E-Prep - Refactor GameState to use full LineupPlayerState objects
**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>
2025-11-03 14:11:40 -06:00
Cal Corum
7417a3f450 Offline catchup 2025-11-03 12:43:54 -06:00
Cal Corum
fc0e2f100c CLAUDE: Integrate X-Check advancement with full GameState support
Updated X-Check runner advancement functions to properly delegate to
existing result handlers for non-error cases.

Changes:
- Updated x_check_g1/g2/g3 signatures to accept GameState, hit_location,
  and defensive_decision parameters
- Updated x_check_f1/f2/f3 signatures to accept GameState and hit_location
- Implemented delegation logic: error cases use simple tables, non-error
  cases delegate to existing tested result handlers (_execute_result,
  _fb_result_*)
- Updated PlayResolver._get_x_check_advancement() to pass new parameters
- Updated all tests to provide required GameState fixtures

Benefits:
- Reuses 13 existing groundball + 4 flyball result handlers (DRY)
- No DP probability needed - X-Check d20 already tested defender
- Full game context: real lineup IDs, outs count, conditional logic
- Error cases remain simple and efficient

Test Results: 264/265 core tests passing (1 pre-existing dice failure)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 00:21:52 -06:00
Cal Corum
5f42576694 CLAUDE: Remove double-dipping on double play probability
Fixed incorrect double play logic that was rolling for probability
twice - once for the chart result and again for execution.

Changes:
- Removed _calculate_double_play_probability() method entirely
- Updated _gb_result_2() to execute DP deterministically
- Updated _gb_result_10() to execute DP deterministically
- Updated _gb_result_13() to execute DP deterministically
- Removed TestDoublePlayProbability test class (5 tests)
- Updated DP tests to reflect deterministic behavior

Logic: Chart already determines outcome via dice roll. When chart
says "Result 2: Double Play", the DP happens (if <2 outs and runner
on 1st exists). No additional probability roll needed.

Tests: 55/55 runner advancement tests passing

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 23:58:19 -06:00
Cal Corum
fb282a5e54 CLAUDE: Fix critical X-Check bugs and improve dice rolling
Fixed two critical bugs in Phase 3D X-Check implementation plus
improved dice audit trail for better tracking.

BUG #1: on_base_code Mapping Error (Sequential vs Bit Field)
============================================================
The implementation incorrectly treated on_base_code as a bit field
when it is actually a sequential lookup mapping.

WRONG (bit field):
  Code 3 (0b011) → R1 + R2
  Code 4 (0b100) → R3 only

CORRECT (sequential):
  Code 3 → R3 only
  Code 4 → R1 + R2

Fixed:
- build_advancement_from_code() decoder (sequential mapping)
- build_flyball_advancement_with_error() decoder (sequential mapping)
- 13 test on_base_code values (3↔4 corrections)
- Updated documentation to clarify NOT a bit field

BUG #2: Table Data Not Matching Official Charts
================================================
7 table entries in G1_ADVANCEMENT_TABLE and G2_ADVANCEMENT_TABLE
did not match the official rulebook charts provided by user.

Fixed table entries:
- G1 Code 1, Infield In: Changed Result 3 → 2
- G1 Code 3, Normal: Changed Result 13 → 3
- G1 Code 3, Infield In: Changed Result 3 → 1
- G1 Code 4, Normal: Changed Result 3 → 13
- G1 Code 4, Infield In: Changed Result 4 → 2
- G2 Code 3, Infield In: Changed Result 3 → 1
- G2 Code 4, Normal: Changed Result 5 → 4

Also fixed 7 test expectations to match corrected tables.

IMPROVEMENT: Better Dice Audit Trail
=====================================
Updated _resolve_x_check() in PlayResolver to use proper
dice_system.roll_fielding() instead of manual die rolling.

Benefits:
- All dice tracked in audit trail (roll_id, timestamp, position)
- Automatic error_total calculation (no manual 3d6 addition)
- Consistent with codebase patterns
- Position recorded for historical analysis

Testing:
- All 59 X-Check advancement tests passing (100%)
- All 9 PlayResolver tests passing (100%)
- All table entries validated against official charts
- Complete codebase scan: no bit field operations found

Files modified:
- backend/app/core/x_check_advancement_tables.py
- backend/tests/unit/core/test_x_check_advancement_tables.py
- backend/app/core/play_resolver.py
- .claude/implementation/PHASE_3D_CRITICAL_FIX.md (documentation)
- .claude/implementation/GROUNDBALL_CHART_REFERENCE.md (new)
- .claude/implementation/XCHECK_TEST_VALIDATION.md (new)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 23:09:16 -06:00
Cal Corum
10515cb20d CLAUDE: Implement Phase 3C - X-Check Resolution Logic
Implemented complete X-Check resolution system in PlayResolver with
defense range and error table lookups.

Changes:
- Added X_CHECK case to resolve_outcome() method
- Implemented _resolve_x_check() main resolution method
- Added _adjust_range_for_defensive_position() for playing in logic
- Added _lookup_defense_table() for defense range table lookups
- Added _apply_hash_conversion() for G2#/G3# to SI2 conversion
- Added _lookup_error_chart() for error determination
- Added _determine_final_x_check_outcome() for final outcome mapping
- Added XCheckResult to PlayResult dataclass
- Integrated all Phase 3B tables (defense, error, holding runners)

Features:
- Full defense table lookup (infield, outfield, catcher)
- Error chart lookup with priority ordering (RP > E3 > E2 > E1 > NO)
- Range adjustment for playing in (+1, max 5)
- Hash conversion based on playing in OR holding runner
- Error overrides outs to ERROR outcome
- Rare play handling
- Detailed X-Check audit trail in XCheckResult

Placeholders (to be completed in later phases):
- Defender retrieval from lineup (currently uses placeholder ratings)
- SPD test implementation (currently defaults to G3)
- Batter handedness from player model
- Runner advancement tables (Phase 3D)

Testing:
- All 9 PlayResolver tests passing
- All 36 X-Check table tests passing
- All 51 runner advancement tests passing
- 325/327 total tests passing (2 pre-existing failures unrelated)
- play_resolver.py compiles successfully

Phase 3C Status: 100% COMPLETE 
Ready for Phase 3D (Runner Advancement Tables)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 14:48:17 -06:00
Cal Corum
0b6076d5b8 CLAUDE: Implement Phase 3B - X-Check league config tables
Complete X-Check resolution table system for defensive play outcomes.

Components:
- Defense range tables (20×5) for infield, outfield, catcher
- Error charts for LF/RF and CF (ratings 0-25)
- Placeholder error charts for P, C, 1B, 2B, 3B, SS (awaiting data)
- get_fielders_holding_runners() - Complete implementation
- get_error_chart_for_position() - Maps all 9 positions
- 6 X-Check placeholder advancement functions (g1-g3, f1-f3)

League Config Integration:
- Both SbaConfig and PdConfig include X-Check tables
- Shared common tables via league_configs.py
- Attributes: x_check_defense_tables, x_check_error_charts, x_check_holding_runners

Testing:
- 36 tests for X-Check tables (all passing)
- 9 tests for X-Check placeholders (all passing)
- Total: 45/45 tests passing

Documentation:
- Updated backend/CLAUDE.md with Phase 3B section
- Updated app/config/CLAUDE.md with X-Check tables documentation
- Updated app/core/CLAUDE.md with X-Check placeholder functions
- Updated tests/CLAUDE.md with new test counts (519 unit tests)
- Updated phase-3b-league-config-tables.md (marked complete)
- Updated NEXT_SESSION.md with Phase 3B completion

What's Pending:
- 6 infield error charts need actual data (P, C, 1B, 2B, 3B, SS)
- Phase 3C will implement full X-Check resolution logic

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 19:50:55 -05:00
Cal Corum
a1f42a93b8 CLAUDE: Implement Phase 3A - X-Check data models and enums
Add foundational data structures for X-Check play resolution system:

Models Added:
- PositionRating: Defensive ratings (range 1-5, error 0-88) for X-Check resolution
- XCheckResult: Dataclass tracking complete X-Check resolution flow with dice rolls,
  conversions (SPD test, G2#/G3#→SI2), error results, and final outcomes
- BasePlayer.active_position_rating: Optional field for current defensive position

Enums Extended:
- PlayOutcome.X_CHECK: New outcome type requiring special resolution
- PlayOutcome.is_x_check(): Helper method for type checking

Documentation Enhanced:
- Play.check_pos: Documented as X-Check position identifier
- Play.hit_type: Documented with examples (single_2_plus_error_1, etc.)

Utilities Added:
- app/core/cache.py: Redis cache key helpers for player positions and game state

Implementation Planning:
- Complete 6-phase implementation plan (3A-3F) documented in .claude/implementation/
- Phase 3A complete with all acceptance criteria met
- Zero breaking changes, all existing tests passing

Next: Phase 3B will add defense tables, error charts, and advancement logic

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 15:32:09 -05:00
Cal Corum
a696473d0a CLAUDE: Integrate flyball advancement with RunnerAdvancement system
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>
2025-10-31 17:04:23 -05:00
Cal Corum
76e24ab22b CLAUDE: Refactor ManualOutcomeSubmission to use PlayOutcome enum + comprehensive documentation
## Refactoring
- Changed `ManualOutcomeSubmission.outcome` from `str` to `PlayOutcome` enum type
- Removed custom validator (Pydantic handles enum validation automatically)
- Added direct import of PlayOutcome (no circular dependency due to TYPE_CHECKING guard)
- Updated tests to use enum values while maintaining backward compatibility

Benefits:
- Better type safety with IDE autocomplete
- Cleaner code (removed 15 lines of validator boilerplate)
- Backward compatible (Pydantic auto-converts strings to enum)
- Access to helper methods (is_hit(), is_out(), etc.)

Files modified:
- app/models/game_models.py: Enum type + import
- tests/unit/config/test_result_charts.py: Updated 7 tests + added compatibility test

## Documentation
Created comprehensive CLAUDE.md files for all backend/app/ subdirectories to help future AI agents quickly understand and work with the code.

Added 8,799 lines of documentation covering:
- api/ (906 lines): FastAPI routes, health checks, auth patterns
- config/ (906 lines): League configs, PlayOutcome enum, result charts
- core/ (1,288 lines): GameEngine, StateManager, PlayResolver, dice system
- data/ (937 lines): API clients (planned), caching layer
- database/ (945 lines): Async sessions, operations, recovery
- models/ (1,270 lines): Pydantic/SQLAlchemy models, polymorphic patterns
- utils/ (959 lines): Logging, JWT auth, security
- websocket/ (1,588 lines): Socket.io handlers, real-time events
- tests/ (475 lines): Testing patterns and structure

Each CLAUDE.md includes:
- Purpose & architecture overview
- Key components with detailed explanations
- Patterns & conventions
- Integration points
- Common tasks (step-by-step guides)
- Troubleshooting with solutions
- Working code examples
- Testing guidance

Total changes: +9,294 lines / -24 lines
Tests: All passing (62/62 model tests, 7/7 ManualOutcomeSubmission tests)
2025-10-31 16:03:54 -05:00
Cal Corum
e2f1d6079f CLAUDE: Implement Week 7 Task 6 - PlayResolver Integration with RunnerAdvancement
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>
2025-10-31 08:20:52 -05:00
Cal Corum
102cbb6081 CLAUDE: Implement Week 7 Tasks 4 & 5 - Runner advancement logic and double play mechanics
- Created runner_advancement.py with complete groundball advancement system
- Implemented GroundballResultType IntEnum with 13 rulebook-aligned results
- Built RunnerAdvancement class with chart lookup logic (Infield Back/In)
- Implemented all 13 result handlers (gb_result_1 through gb_result_13)
- Added DECIDE mechanic support for interactive runner advancement decisions
- Implemented double play probability calculation with positioning modifiers
- Created 30 comprehensive unit tests covering all scenarios (100% passing)

Key Features:
- Supports Infield Back and Infield In defensive positioning
- Handles Corners In hybrid positioning (applies In rules to corner fielders)
- Conditional results based on hit location (middle IF, right side, corners)
- Force play detection and advancement logic
- Double play mechanics with probability-based success (45% base rate)
- Result types match official rulebook exactly (1-13)

Architecture:
- IntEnum for result types (type-safe, self-documenting)
- Comprehensive hit location tracking (1B, 2B, SS, 3B, P, C, LF, CF, RF)
- Dataclasses for movements (RunnerMovement, AdvancementResult)
- Probability modifiers: Infield In (-15%), hit location (±10%)

Testing:
- 30 unit tests covering chart lookup, all result types, and edge cases
- Double play probability validation
- All on-base codes (0-7) tested
- All groundball types (A, B, C) verified

Status: Week 7 Tasks 4 & 5 complete (~87% of Week 7 finished)
Next: Task 6 (PlayResolver Integration)

Related: #task4 #task5 #runner-advancement #double-play #week7
2025-10-30 23:32:44 -05:00
Cal Corum
9cae63ac43 CLAUDE: Implement Week 7 Task 7 - WebSocket manual outcome handlers
Complete manual outcome workflow for SBA and PD manual mode gameplay:

**WebSocket Event Handlers** (app/websocket/handlers.py):
- roll_dice: Server rolls dice, stores in state, broadcasts to players
- submit_manual_outcome: Validates and processes player submissions
- Events: dice_rolled, outcome_accepted, outcome_rejected, play_resolved

**Game Engine Integration** (app/core/game_engine.py):
- resolve_manual_play(): Processes manual outcomes with server dice
- Uses ab_roll for audit trail, player outcome for resolution
- Same orchestration as resolve_play() (save, update, advance inning)

**Data Model** (app/models/game_models.py):
- pending_manual_roll: Stores server dice between roll and submission

**Terminal Client** (terminal_client/):
- roll_dice command: Roll dice and display results
- manual_outcome command: Submit outcomes from physical cards
- Both integrated into REPL for testing

**Tests** (tests/unit/websocket/test_manual_outcome_handlers.py):
- 12 comprehensive tests covering all validation paths
- All tests passing (roll_dice: 4, submit_manual_outcome: 8)

**Key Decisions**:
- Server rolls dice for fairness (not players!)
- One-time roll usage (cleared after submission)
- Early validation (check pending roll before accepting)
- Field-level error messages for clear feedback

**Impact**:
- Complete manual mode workflow ready
- Frontend WebSocket integration supported
- Terminal testing commands available
- Audit trail with server-rolled dice maintained

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 22:51:31 -05:00
Cal Corum
9b03fb555b CLAUDE: Implement play rollback functionality for error recovery
Add ability to roll back the last N plays, useful for correcting mistakes
or recovering from corrupted plays. Deletes plays from database and
reconstructs game state by replaying remaining plays.

Database Operations (app/database/operations.py):
- delete_plays_after(): Delete plays with play_number > target
- delete_substitutions_after(): Delete lineup entries with after_play >= target
- delete_rolls_after(): Delete dice rolls (kept for reference, not used)

Game Engine (app/core/game_engine.py):
- rollback_plays(): Main rollback orchestration
  - Validates: num_plays > 0, enough plays exist, game not completed
  - Deletes plays and substitutions from database
  - Clears in-memory roll tracking
  - Calls state_manager.recover_game() to rebuild state
  - Returns updated GameState

Terminal Client (terminal_client/commands.py, terminal_client/repl.py):
- rollback_plays(): Command wrapper with user-friendly output
- do_rollback(): REPL command with argument parsing

Usage:
   > rollback 3

Validations:
- Cannot roll back more plays than exist
- Cannot roll back completed games
- Rolling back across innings is allowed
- Substitutions after rolled-back plays are undone

Testing:
-  Successfully rolls back 2 plays from 5-play game
-  Correctly validates rollback of 10 plays when only 2 exist
-  Game state properly reconstructed via replay

Note: Dice rolls kept in database for auditing (don't affect state).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:02:51 -05:00
Cal Corum
8ecce0f5ad CLAUDE: Implement forced outcome feature for terminal client testing
Add ability to force specific play outcomes instead of random dice rolls,
enabling targeted testing of specific game scenarios.

Changes:
- play_resolver.resolve_play(): Add forced_outcome parameter, bypass dice
  rolls when provided, create dummy AbRoll with placeholder values
- game_engine.resolve_play(): Accept and pass through forced_outcome param
- terminal_client/commands.py: Pass forced_outcome to game engine

Testing:
- Verified TRIPLE, HOMERUN, and STRIKEOUT outcomes work correctly
- Dummy AbRoll properly constructed with all required fields
- Game state updates correctly with forced outcomes

Example usage in REPL:
  resolve_with triple
  resolve_with homerun

Fixes terminal client testing workflow to allow controlled scenarios.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 15:39:35 -05:00
Cal Corum
c0051d2a65 CLAUDE: Fix defensive decision validation for corners_in/infield_in depths
- Updated validators.py to use is_runner_on_third() helper method instead of hardcoded on_base_code values
- Fixed DefensiveDecision Pydantic model: infield depths now ['infield_in', 'normal', 'corners_in']
- Fixed DefensiveDecision Pydantic model: outfield depths now ['in', 'normal'] (removed 'back')
- Removed invalid double_play depth tests (depth doesn't exist)
- Added proper tests for corners_in and infield_in validation (requires runner on third)
- All 54 validator tests now passing

Changes maintain consistency between Pydantic validation and GameValidator logic.
2025-10-30 10:25:01 -05:00
Cal Corum
121a9082f1 CLAUDE: Implement Week 7 Task 2 - Decision Validators
- Enhanced validate_defensive_decision() with comprehensive validation:
  - Validate all alignments (normal, shifted_left, shifted_right, extreme_shift)
  - Validate all infield depths (in, normal, back, double_play)
  - Validate all outfield depths (in, normal, back)
  - Validate hold_runners require actual runners on specified bases
  - Validate hold_runners only on bases 1, 2, or 3
  - Validate double_play depth requires runner on first
  - Validate double_play depth not allowed with 2 outs

- Enhanced validate_offensive_decision() with comprehensive validation:
  - Validate all approaches (normal, contact, power, patient)
  - Validate steal_attempts only to bases 2, 3, or 4
  - Validate steal_attempts require runner on base-1
  - Validate bunt_attempt not allowed with 2 outs
  - Validate bunt_attempt and hit_and_run cannot be simultaneous
  - Validate hit_and_run requires at least one runner on base

- Added 24+ comprehensive test cases covering all edge cases:
  - 13 new defensive decision validation tests
  - 16 new offensive decision validation tests
  - All tests pass (54/54 passing)

Clear error messages for all validation failures.
Follows 'Raise or Return' pattern with ValidationError exceptions.
2025-10-30 06:38:34 -05:00