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>
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>
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>
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>
Added documentation for 8 X-Check testing and gameplay commands that
were implemented but missing from the help system:
X-Check Testing Commands:
- roll_jump: Roll jump dice for stolen base testing
- test_jump: Test jump roll distribution statistics
- roll_fielding: Roll fielding check dice for X-Check defensive plays
- test_fielding: Test fielding roll distribution
Testing & Development:
- test_location: Test hit location distribution
- rollback: Roll back the last N plays
Interrupt Plays:
- force_wild_pitch: Force a wild pitch interrupt play
- force_passed_ball: Force a passed ball interrupt play
Changes:
- help_text.py: Added new command categories to show_command_list()
- "X-Check Testing" section with 4 dice testing commands
- "Interrupt Plays" section with 2 force commands
- Moved test_location and rollback to "Testing & Development"
- help_text.py: Added detailed HELP_DATA entries for all 8 commands
- Complete usage, options, examples, and notes for each
- Includes dice component explanations and expected distributions
Users can now run 'help' to see all commands or 'help <command>' for
detailed information about X-Check testing features.
🤖 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>
Updated terminal_client/CLAUDE.md to use UV commands.
## Changes
- Updated REPL start command: uv run python -m terminal_client
- Updated standalone commands to use uv run prefix
- Added alternative manual activation option
- Consistent with all other project documentation
🤖 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>
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>
Remove unused PlayResult creation code that had incorrect import path
and missing required fields. The forced outcome feature is experimental
and not yet implemented - the code was just showing warnings anyway.
Fixes ImportError when running 'resolve_with <outcome>' command.
🤖 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>
This commit includes Week 6 player models implementation and critical
performance optimizations discovered during testing.
## Player Models (Week 6 - 50% Complete)
**New Files:**
- app/models/player_models.py (516 lines)
- BasePlayer abstract class with polymorphic interface
- SbaPlayer with API parsing factory method
- PdPlayer with batting/pitching scouting data support
- Supporting models: PdCardset, PdRarity, PdBattingCard, PdPitchingCard
- tests/unit/models/test_player_models.py (692 lines)
- 32 comprehensive unit tests, all passing
- Tests for BasePlayer, SbaPlayer, PdPlayer, polymorphism
**Architecture:**
- Simplified single-layer approach vs planned two-layer
- Factory methods handle API → Game transformation directly
- SbaPlayer.from_api_response(data) - parses SBA API inline
- PdPlayer.from_api_response(player_data, batting_data, pitching_data)
- Full Pydantic validation, type safety, and polymorphism
## Performance Optimizations
**Database Query Reduction (60% fewer queries per play):**
- Before: 5 queries per play (INSERT play, SELECT play with JOINs,
SELECT games, 2x SELECT lineups)
- After: 2 queries per play (INSERT play, UPDATE games conditionally)
Changes:
1. Lineup caching (game_engine.py:384-425)
- Check state_manager.get_lineup() cache before DB fetch
- Eliminates 2 SELECT queries per play
2. Remove unnecessary refresh (operations.py:281-302)
- Removed session.refresh(play) after INSERT
- Eliminates 1 SELECT with 3 expensive LEFT JOINs
3. Direct UPDATE statement (operations.py:109-165)
- Changed update_game_state() to use direct UPDATE
- No longer does SELECT + modify + commit
4. Conditional game state updates (game_engine.py:200-217)
- Only UPDATE games table when score/inning/status changes
- Captures state before/after and compares
- ~40-60% fewer updates (many plays don't score)
## Bug Fixes
1. Fixed outs_before tracking (game_engine.py:551)
- Was incorrectly calculating: state.outs - result.outs_recorded
- Now correctly captures: state.outs (before applying result)
- All play records now have accurate out counts
2. Fixed game recovery (state_manager.py:312-314)
- AttributeError when recovering: 'GameState' has no attribute 'runners'
- Changed to use state.get_all_runners() method
- Games can now be properly recovered from database
## Enhanced Terminal Client
**Status Display Improvements (terminal_client/display.py:75-97):**
- Added "⚠️ WAITING FOR ACTION" section when play is pending
- Shows specific guidance:
- "The defense needs to submit their decision" → Run defensive [OPTIONS]
- "The offense needs to submit their decision" → Run offensive [OPTIONS]
- "Ready to resolve play" → Run resolve
- Color-coded command hints for better UX
## Documentation Updates
**backend/CLAUDE.md:**
- Added comprehensive Player Models section (204 lines)
- Updated Current Phase status to Week 6 (~50% complete)
- Documented all optimizations and bug fixes
- Added integration examples and usage patterns
**New Files:**
- .claude/implementation/week6-status-assessment.md
- Comprehensive Week 6 progress review
- Architecture decision rationale (single-layer vs two-layer)
- Completion status and next priorities
- Updated roadmap for remaining Week 6 work
## Test Results
- Player models: 32/32 tests passing
- All existing tests continue to pass
- Performance improvements verified with terminal client
## Next Steps (Week 6 Remaining)
1. Configuration system (BaseConfig, SbaConfig, PdConfig)
2. Result charts & PD play resolution with ratings
3. API client for live roster data (deferred)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>