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>
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>
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>
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>
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>
New Commands:
- manual_outcome <outcome> [location] - Validates ManualOutcomeSubmission
- Tests outcome and location validation
- Shows which outcomes require hit location
- Displays clear validation errors
- test_location <outcome> [handedness] [count] - Tests hit location distribution
- Generates sample hit locations for an outcome
- Shows distribution table with percentages
- Validates pull rates (45% pull, 35% center, 20% opposite)
- Supports both LHB and RHB
Implementation:
- Added validate_manual_outcome() to GameCommands class
- Added test_hit_location() to GameCommands class
- Added do_manual_outcome() to REPL
- Added do_test_location() to REPL
- Uses ManualOutcomeSubmission model from Task 3
- Uses calculate_hit_location() helper from Task 3
Testing:
- Tested manual_outcome with valid outcomes (groundball_c SS, strikeout)
- Tested manual_outcome with invalid outcome (proper error display)
- Tested test_location with groundball_c for RHB (shows distribution)
- All validation and display working correctly
Note: Full play resolution integration deferred to Week 7 Task 6 (WebSocket handlers).
These commands validate and test the new models but don't resolve plays yet.
Files Modified:
- terminal_client/commands.py (+117 lines)
- terminal_client/repl.py (+65 lines)
Terminal Client Enhancements:
- Added list_outcomes command to display all PlayOutcome values
- Added resolve_with <outcome> command for testing specific scenarios
- TAB completion for all outcome names
- Full help documentation and examples
- Infrastructure ready for Week 7 integration
Files Modified:
- terminal_client/commands.py - list_outcomes() and forced outcome support
- terminal_client/repl.py - do_list_outcomes() and do_resolve_with() commands
- terminal_client/completions.py - VALID_OUTCOMES and complete_resolve_with()
- terminal_client/help_text.py - Help entries for new commands
Phase 3 Planning:
- Created comprehensive Week 7 implementation plan (25 pages)
- 6 major tasks covering strategic decisions and result charts
- Updated 00-index.md to mark Week 6 as 100% complete
- Documented manual outcome testing feature
Week 6: 100% Complete ✅
Phase 3 Week 7: Ready to begin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit includes cleanup from model refactoring and terminal client
modularization for better code organization and maintainability.
## Game Models Refactor
**Removed RunnerState class:**
- Eliminated separate RunnerState model (was redundant)
- Replaced runners: List[RunnerState] with direct base references:
- on_first: Optional[LineupPlayerState]
- on_second: Optional[LineupPlayerState]
- on_third: Optional[LineupPlayerState]
- Updated helper methods:
- get_runner_at_base() now returns LineupPlayerState directly
- get_all_runners() returns List[Tuple[int, LineupPlayerState]]
- is_runner_on_X() simplified to direct None checks
**Benefits:**
- Matches database structure (plays table has on_first_id, etc.)
- Simpler state management (direct references vs list management)
- Better type safety (LineupPlayerState vs generic runner)
- Easier to work with in game engine logic
**Updated files:**
- app/models/game_models.py - Removed RunnerState, updated GameState
- app/core/play_resolver.py - Use get_all_runners() instead of state.runners
- app/core/validators.py - Updated runner access patterns
- tests/unit/models/test_game_models.py - Updated test assertions
- tests/unit/core/test_play_resolver.py - Updated test data
- tests/unit/core/test_validators.py - Updated test data
## Terminal Client Refactor
**Modularization (DRY principle):**
Created separate modules for better code organization:
1. **terminal_client/commands.py** (10,243 bytes)
- Shared command functions for game operations
- Used by both CLI (main.py) and REPL (repl.py)
- Functions: submit_defensive_decision, submit_offensive_decision,
resolve_play, quick_play_sequence
- Single source of truth for command logic
2. **terminal_client/arg_parser.py** (7,280 bytes)
- Centralized argument parsing and validation
- Handles defensive/offensive decision arguments
- Validates formats (alignment, depths, hold runners, steal attempts)
3. **terminal_client/completions.py** (10,357 bytes)
- TAB completion support for REPL mode
- Command completions, option completions, dynamic completions
- Game ID completions, defensive/offensive option suggestions
4. **terminal_client/help_text.py** (10,839 bytes)
- Centralized help text and command documentation
- Detailed command descriptions
- Usage examples for all commands
**Updated main modules:**
- terminal_client/main.py - Simplified by using shared commands module
- terminal_client/repl.py - Cleaner with shared functions and completions
**Benefits:**
- DRY: Behavior consistent between CLI and REPL modes
- Maintainability: Changes in one place affect both interfaces
- Testability: Can test commands module independently
- Organization: Clear separation of concerns
## Documentation
**New files:**
- app/models/visual_model_relationships.md
- Visual documentation of model relationships
- Helps understand data flow between models
- terminal_client/update_docs/ (6 phase documentation files)
- Phased documentation for terminal client evolution
- Historical context for implementation decisions
## Tests
**New test files:**
- tests/unit/terminal_client/__init__.py
- tests/unit/terminal_client/test_arg_parser.py
- tests/unit/terminal_client/test_commands.py
- tests/unit/terminal_client/test_completions.py
- tests/unit/terminal_client/test_help_text.py
**Updated tests:**
- Integration tests updated for new runner model
- Unit tests updated for model changes
- All tests passing with new structure
## Summary
- ✅ Simplified game state model (removed RunnerState)
- ✅ Better alignment with database structure
- ✅ Modularized terminal client (DRY principle)
- ✅ Shared command logic between CLI and REPL
- ✅ Comprehensive test coverage
- ✅ Improved documentation
Total changes: 26 files modified/created
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical bugs in game recovery and play persistence:
1. Terminal REPL Auto-Recovery:
- Added _ensure_game_loaded() helper to auto-recover games from database
- Calls state_manager.recover_game() when game not in memory
- Calls _prepare_next_play() after recovery to populate snapshot fields
- Enables seamless continuation of games across REPL sessions
2. Play Validation:
- Added verification in _save_play_to_db() for required fields
- Ensures batter_id, pitcher_id, catcher_id are never NULL
- Raises ValueError with clear error message if fields missing
- Prevents database constraint violations
3. Updated Commands:
- All REPL commands now call _ensure_game_loaded()
- Commands: defensive, offensive, resolve, status, quick_play, box_score
- Fixes "Game state not found" errors on recovered games
Root Cause:
- state_manager.recover_game() rebuilds GameState from database
- But didn't populate snapshot fields (current_batter_lineup_id, etc.)
- _save_play_to_db() requires these fields to save plays
- Solution: Call _prepare_next_play() after recovery
Files Modified:
- app/core/game_engine.py - Added verification in _save_play_to_db()
- terminal_client/repl.py - Added _ensure_game_loaded() and integrated
Testing: Successfully recovered game, submitted decisions, and resolved plays
🚀 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>