CLAUDE: Update project plan for next session - Substitution system completion

Generated comprehensive NEXT_SESSION.md with:
- Complete analysis of Phase 3E-Final and Substitution core logic
- 4 detailed tasks with code examples and acceptance criteria
- Outstanding questions and architecture decisions documented
- 6-7 hour estimate to complete remaining 40%

Next session will complete:
- WebSocket substitution events (2-3 hrs)
- Validation unit tests (2 hrs)
- Manager integration tests (2 hrs)
- API documentation (1 hr)

Phase 3 will be ~99% complete after next session.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-11-04 00:08:53 -06:00
parent d1619b4a1f
commit 5ebbd9ebda

View File

@ -1,226 +1,798 @@
# Next Session - Phase 3E: X-Check Position Ratings # Next Session Plan - Phase 3: Substitution System Completion
## Current Status: Phase 3E-Main Complete ✅ **Current Status**: Phase 3 - 60% Substitution System Complete
**Last Commit**: `d1619b4` - "CLAUDE: Phase 3 - Substitution System Core Logic"
**Overall Phase 3E Progress: 90%** **Date**: 2025-11-03
**Remaining Work**: 40% (WebSocket events, tests, documentation)
### ✅ Phase 3E-Main: Position Ratings Integration (COMPLETED)
**Completion Date**: 2025-11-03
**Commit**: `02e816a` - CLAUDE: Phase 3E-Main - Position ratings integration for X-Check resolution
#### What Was Accomplished
1. **PD API Client Integration**
- Created `app/services/pd_api_client.py` - async HTTP client using httpx
- Endpoint: `GET https://pd.manticorum.com/api/v2/cardpositions?player_id={id}`
- Supports position filtering via query parameters
- Tested with real API data (player 8807 - 7 positions)
2. **Position Rating Service with Caching**
- Created `app/services/position_rating_service.py`
- In-memory caching (16,601x speedup: 0.214s API → 0.000s cache)
- League-aware: PD fetches ratings, SBA returns empty list
- Graceful degradation on API errors
3. **GameState Integration**
- Added `position_rating` field to `LineupPlayerState`
- Added `get_defender_for_position()` using StateManager lineup cache
- Self-contained X-Check data - no lookups during resolution
4. **League Config Pattern**
- Added `supports_position_ratings()` to both league configs
- PD: Returns True (uses API)
- SBA: Returns False (uses defaults)
5. **PlayResolver Updates**
- Integrated StateManager for O(1) defender lookups
- Uses actual position ratings when available
- Falls back to defaults (range=3, error=15) for SBA or missing data
6. **Game Engine Updates**
- Added `_load_position_ratings_for_lineup()` method
- Loads ratings at game start for both teams (PD league only)
- Updated PlayResolver instantiation with state_manager
7. **Comprehensive Testing**
- Live API integration test (`test_pd_api_live.py`)
- Mock API test for CI/CD (`test_pd_api_mock.py`)
- Pytest integration suite (`tests/integration/test_position_ratings_api.py`)
- Verified full flow: API → Cache → GameState → X-Check resolution
#### Live Test Results
**Player 8807** (7 positions verified):
```
Position Range Error Arm Innings
CF 3 2 3 372
2B 3 8 3 212
SS 4 12 4 159
RF 2 2 2 74
LF 3 2 3 62
1B 4 0 3 46
3B 3 65 2 34
```
**Performance Metrics**:
- API call: 0.214s
- Cache hit: 0.000013s
- Speedup: 16,601x
--- ---
## 🎯 Phase 3E-Final: Remaining Work (10%) ## Quick Start for Next AI Agent
### Tasks to Complete ### 🎯 Where to Begin
1. Read this entire document first
2. Review `.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md` for architecture overview
3. Review core logic files in "Files to Review Before Starting" section
4. Start with Task 1: WebSocket Events (highest user impact)
5. Run test commands after each task
#### 1. WebSocket Event Handlers for X-Check UI ### 📍 Current Context
**Priority**: HIGH
**Estimate**: 2-3 hours
- [ ] Create `handle_xcheck_confirm` event handler We just completed the **core business logic** for the substitution system (1,027 lines). The validation rules, database operations, and state management are fully implemented and follow the established DB-first pattern. What remains is **integration** (WebSocket events for real-time gameplay) and **verification** (comprehensive testing).
- [ ] Emit `xcheck_result` events with defender ratings
- [ ] Update frontend WebSocket listeners
- [ ] Test real-time X-Check flow end-to-end
**Files to Modify**: Phase 3E (X-Check system with position ratings and Redis caching) is **100% complete**. Phase 3 overall is at ~98% for X-Check work, with substitutions now being the active focus.
- `app/websocket/game_events.py`
- Frontend Socket.io listeners (both leagues)
#### 2. Upgrade to Redis Caching ---
**Priority**: MEDIUM
**Estimate**: 3-4 hours
- [ ] Add redis-py to requirements.txt ## What We Just Completed ✅
- [ ] Create Redis connection pool in app startup
- [ ] Migrate `position_rating_service.py` from in-memory to Redis
- [ ] Set TTL on cached ratings (e.g., 24 hours)
- [ ] Add Redis cache warming on game start
- [ ] Test cache invalidation and recovery
**Files to Modify**: ### 1. Phase 3E-Final: Redis Caching & X-Check WebSocket Integration (adf7c76)
- `requirements.txt` - `app/services/redis_client.py` - Async Redis client with connection pooling
- `app/main.py` (startup/shutdown events) - `app/services/position_rating_service.py` - Migrated from in-memory to Redis (760x speedup)
- `app/services/position_rating_service.py` - `app/main.py` - Redis startup/shutdown lifecycle
- `app/config.py` - Added redis_url setting
- `app/websocket/handlers.py` - Enhanced submit_manual_outcome with X-Check details
- `app/websocket/X_CHECK_FRONTEND_GUIDE.md` - 517 lines of frontend integration docs
- `app/websocket/MANUAL_VS_AUTO_MODE.md` - 588 lines of workflow documentation
- `tests/integration/test_xcheck_websocket.py` - WebSocket integration tests (2 tests)
- `test_redis_cache.py` - Live Redis integration test (10 verification steps)
- **Performance**: 760x speedup (0.274s API → 0.000361s Redis)
- **Tests**: 2/2 WebSocket tests passing, Redis live test validated
**Technical Notes**: ### 2. Substitution System Core Logic (d1619b4) - **Just Completed**
#### SubstitutionRules - Validation Logic (345 lines)
- `backend/app/core/substitution_rules.py` - Complete validation for all substitution types
- **Methods**:
- `validate_pinch_hitter()` - Validates pinch hitter substitutions
- `validate_defensive_replacement()` - Validates defensive replacements
- `validate_pitching_change()` - Validates pitching changes
- `validate_double_switch()` - Validates complex double switches (2 simultaneous subs)
- **Rules Enforced**:
- No re-entry (once removed, cannot return)
- Roster eligibility (must be on roster)
- Active status (substitute must be inactive)
- Current batter check (pinch hitter only for current batter)
- Minimum batters faced (pitcher must face 1 batter unless injury)
- Position validation (valid baseball positions)
- Timing checks (warnings for mid-inning changes)
#### SubstitutionManager - Orchestration Logic (552 lines)
- `backend/app/core/substitution_manager.py` - Orchestrates DB-first pattern
- **Methods**:
- `pinch_hit()` - Execute pinch hitter substitution
- `defensive_replace()` - Execute defensive replacement
- `change_pitcher()` - Execute pitching change
- **Pattern**: Validate (in-memory) → Update DATABASE FIRST → Update state SECOND → Return result
- **Features**:
- Comprehensive error handling with error codes
- Automatic lineup cache updates
- Current player references updated (batter/pitcher/catcher in GameState)
- Detailed logging at every step
#### Database Operations (+115 lines)
- `backend/app/database/operations.py` - Added substitution-specific operations
- **Methods**:
- `create_substitution()` - Marks old player inactive, creates new lineup entry
- `get_eligible_substitutes()` - Gets inactive players (potential subs)
- **Database Fields Used**:
- `is_active`: Tracks current vs benched
- `is_starter`: Original vs substitute
- `entered_inning`: When player entered
- `replacing_id`: Links to replaced player
- `after_play`: Exact play number of substitution
#### Model Enhancements (+15 lines)
- `backend/app/models/game_models.py` - Added helper method
- `get_player_by_card_id()` in TeamLineupState - Find player by card/player ID
### 3. Documentation
- `.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md` - Complete architecture and status doc
- Detailed implementation notes, integration points, testing strategy
---
## Key Architecture Decisions Made
### 1. **DB-First Pattern for Substitutions**
Following established game engine pattern:
```
1. Validate (in-memory, fast)
2. Update DATABASE (source of truth)
3. Update STATE (cache)
4. Broadcast (WebSocket in handler)
```
**Rationale**: Database survives server restarts; in-memory state is just cache
### 2. **Immutable Lineup History**
- Never delete lineup entries
- Use `is_active` flag for current lineup
- `replacing_id` creates complete audit trail
**Benefits**: Can reconstruct game at any point; rollback support; complete substitution history
### 3. **Position Flexibility (MVP)**
- Don't enforce strict position eligibility in MVP
- Any player can play any position
- Can add validation post-MVP
**Rationale**: Simplifies MVP; users know their rosters; reduces complexity
### 4. **Batting Order Inheritance**
- Pinch hitter takes batting order of replaced player
- Defensive replacement keeps batting order if in lineup
- Double switch allows batting order changes
**Rationale**: Matches real baseball rules
### 5. **Error Codes for All Validation Failures**
Every validation failure returns both human-readable message and machine-readable code:
- `NOT_CURRENT_BATTER`, `PLAYER_ALREADY_OUT`, `NOT_IN_ROSTER`, `ALREADY_ACTIVE`, etc.
**Rationale**: Frontend can show appropriate UI based on error type
---
## Blockers Encountered 🚧
**None** - development proceeded smoothly.
Core logic implementation was straightforward following established patterns. All validation rules are clear and testable. Database operations follow proven async session pattern.
---
## Outstanding Questions ❓
### 1. **Should we implement double switch in MVP?**
**Context**: Double switch is complex (2 simultaneous subs with batting order swap). Core logic exists but no WebSocket events yet.
**Decision Needed**: MVP or post-MVP?
**Recommendation**: **Post-MVP** - rarely used, complex UX, single subs cover 95% of use cases
### 2. **Position eligibility validation - when to add?**
**Context**: Currently any player can play any position (MVP simplification).
**Decision Needed**: Add position eligibility check post-MVP?
**Implication**: Would require position lists on player models, more complex validation
### 3. **Automatic pitcher fatigue system - scope?**
**Context**: Database has `is_fatigued` flag on Lineup model.
**Decision Needed**: Implement automatic fatigue detection or manual only?
**Recommendation**: **Manual only for MVP** - managers know their staff better than algorithm
---
## Tasks for Next Session
### Task 1: WebSocket Substitution Events (2-3 hours)
**File(s)**: `backend/app/websocket/handlers.py`
**Goal**: Add WebSocket event handlers for pinch hitting, defensive replacements, and pitching changes. Enable real-time substitution notifications to all game clients.
**Changes**:
1. **Import SubstitutionManager** at top of file:
```python ```python
# Redis key pattern: "position_ratings:{card_id}:{position}" from app.core.substitution_manager import SubstitutionManager
# TTL: 86400 seconds (24 hours)
# Store as JSON serialized PositionRating.model_dump()
``` ```
#### 3. Full Defensive Lineup in GameState 2. **Initialize in GameEngine** (if not already there):
**Priority**: LOW ```python
**Estimate**: 1-2 hours # In GameEngine.__init__()
self.substitution_manager = SubstitutionManager(self.db_ops)
```
Currently only `current_pitcher` and `current_catcher` are direct fields. 3. **Add event handler for pinch hitter**:
Consider adding full defensive positions for easier access: ```python
@sio.event
async def request_pinch_hitter(sid, data):
"""
Request pinch hitter substitution.
- [ ] Add optional `defensive_positions: Dict[str, int]` to GameState Data:
- [ ] Map position → lineup_id for all 9 fielders - game_id: UUID
- [ ] Update on lineup changes and substitutions - player_out_lineup_id: int
- [ ] Evaluate if this improves performance vs current StateManager lookup - player_in_card_id: int
- team_id: int
"""
try:
game_id = UUID(data['game_id'])
**Evaluation Needed**: May not be necessary since StateManager lookup is already O(1). # Verify user has access to game and team
# TODO: Add authorization check
#### 4. Manual vs Auto Mode X-Check Workflows result = await game_engine.substitution_manager.pinch_hit(
**Priority**: MEDIUM game_id=game_id,
**Estimate**: 2 hours player_out_lineup_id=data['player_out_lineup_id'],
player_in_card_id=data['player_in_card_id'],
team_id=data['team_id']
)
- [ ] Document manual mode flow (player confirms chart reads) if result.success:
- [ ] Document auto mode flow (immediate resolution) # Broadcast to all clients in game
- [ ] Ensure UI shows defender ratings in both modes await manager.emit_to_game(
- [ ] Add confirmation step in manual mode before advancing game_id,
'player_substituted',
{
'type': 'pinch_hitter',
'player_out_lineup_id': result.player_out_lineup_id,
'player_in_card_id': result.player_in_card_id,
'new_lineup_id': result.new_lineup_id,
'position': result.new_position,
'batting_order': result.new_batting_order,
'team_id': data['team_id']
}
)
# Send success to requester
await sio.emit('substitution_confirmed', {
'new_lineup_id': result.new_lineup_id
}, room=sid)
else:
# Send error to requester
await sio.emit('substitution_error', {
'error': result.error_message,
'code': result.error_code
}, room=sid)
except Exception as e:
logger.error(f"Error in pinch hitter request: {e}", exc_info=True)
await sio.emit('error', {'message': str(e)}, room=sid)
```
4. **Add similar handlers for**:
- `request_defensive_replacement` (similar structure)
- `request_pitching_change` (similar structure)
5. **Add get_lineup event** for UI to request current lineup:
```python
@sio.event
async def get_lineup(sid, data):
"""Get current active lineup for a team."""
try:
game_id = UUID(data['game_id'])
team_id = data['team_id']
lineup = state_manager.get_lineup(game_id, team_id)
if lineup:
await sio.emit('lineup_data', {
'team_id': team_id,
'players': [p.model_dump() for p in lineup.players if p.is_active]
}, room=sid)
else:
await sio.emit('error', {
'message': f'Lineup not found for team {team_id}'
}, room=sid)
except Exception as e:
logger.error(f"Error getting lineup: {e}", exc_info=True)
await sio.emit('error', {'message': str(e)}, room=sid)
```
**Files to Update**:
- `backend/app/websocket/handlers.py` - Add 4 event handlers
**Test Command**:
```bash
# Manual test with terminal client
python -m terminal_client
# Then in REPL, test substitution flow
```
**Acceptance Criteria**:
- [ ] `request_pinch_hitter` event handler implemented
- [ ] `request_defensive_replacement` event handler implemented
- [ ] `request_pitching_change` event handler implemented
- [ ] `get_lineup` event handler implemented
- [ ] Successful substitutions broadcast to all clients
- [ ] Errors sent only to requester
- [ ] No crashes on invalid data
--- ---
## 📊 Phase 3 Overall Progress ### Task 2: Substitution Validation Tests (2 hours)
| Phase | Status | Progress | **File(s)**: `backend/tests/unit/core/test_substitution_rules.py` (NEW)
|-------|--------|----------|
| 3A: Core Models | ✅ Complete | 100% | **Goal**: Comprehensive unit tests for all validation rules in SubstitutionRules class.
| 3B: Play Validation | ✅ Complete | 100% |
| 3C: Result Charts | ✅ Complete | 100% | **Changes**:
| 3D: Dice & Resolution | ✅ Complete | 100% |
| 3E-Prep: Refactoring | ✅ Complete | 100% | Create new test file with pytest structure:
| **3E-Main: Position Ratings** | ✅ Complete | **100%** |
| **3E-Final: UI & Redis** | ⏳ In Progress | **0%** | ```python
| **Overall Phase 3** | ⏳ In Progress | **~95%** | """
Unit tests for SubstitutionRules validation logic.
"""
import pytest
from uuid import uuid4
from app.core.substitution_rules import SubstitutionRules, ValidationResult
from app.models.game_models import GameState, LineupPlayerState, TeamLineupState
@pytest.fixture
def game_state():
"""Create test game state."""
state = GameState(
game_id=uuid4(),
league_id='sba',
home_team_id=1,
away_team_id=2,
current_batter_lineup_id=10
)
return state
@pytest.fixture
def roster():
"""Create test roster."""
players = [
LineupPlayerState(lineup_id=10, card_id=101, position='CF', batting_order=1, is_active=True),
LineupPlayerState(lineup_id=11, card_id=102, position='SS', batting_order=2, is_active=True),
LineupPlayerState(lineup_id=12, card_id=103, position='P', batting_order=9, is_active=True),
# Bench players
LineupPlayerState(lineup_id=20, card_id=201, position='CF', batting_order=None, is_active=False),
LineupPlayerState(lineup_id=21, card_id=202, position='SS', batting_order=None, is_active=False),
]
return TeamLineupState(team_id=1, players=players)
class TestPinchHitterValidation:
def test_success(self, game_state, roster):
"""Test successful pinch hitter validation."""
player_out = roster.get_player_by_lineup_id(10)
result = SubstitutionRules.validate_pinch_hitter(
state=game_state,
player_out=player_out,
player_in_card_id=201,
roster=roster
)
assert result.is_valid
assert result.error_message is None
def test_not_current_batter(self, game_state, roster):
"""Test pinch hit fails when not current batter."""
player_out = roster.get_player_by_lineup_id(11) # Not current batter
result = SubstitutionRules.validate_pinch_hitter(
state=game_state,
player_out=player_out,
player_in_card_id=201,
roster=roster
)
assert not result.is_valid
assert result.error_code == "NOT_CURRENT_BATTER"
def test_substitute_not_in_roster(self, game_state, roster):
"""Test pinch hit fails when substitute not in roster."""
player_out = roster.get_player_by_lineup_id(10)
result = SubstitutionRules.validate_pinch_hitter(
state=game_state,
player_out=player_out,
player_in_card_id=999, # Not in roster
roster=roster
)
assert not result.is_valid
assert result.error_code == "NOT_IN_ROSTER"
def test_substitute_already_active(self, game_state, roster):
"""Test pinch hit fails when substitute already in game."""
player_out = roster.get_player_by_lineup_id(10)
result = SubstitutionRules.validate_pinch_hitter(
state=game_state,
player_out=player_out,
player_in_card_id=102, # Already active
roster=roster
)
assert not result.is_valid
assert result.error_code == "ALREADY_ACTIVE"
class TestDefensiveReplacementValidation:
# Similar structure, test all validation paths
pass
class TestPitchingChangeValidation:
# Similar structure, test minimum batters faced, etc.
pass
```
**Files to Create**:
- `backend/tests/unit/core/test_substitution_rules.py` (new file, ~300 lines)
**Test Command**:
```bash
cd backend
pytest tests/unit/core/test_substitution_rules.py -v
```
**Acceptance Criteria**:
- [ ] 15+ tests for pinch hitter validation (all validation paths)
- [ ] 12+ tests for defensive replacement validation
- [ ] 10+ tests for pitching change validation
- [ ] 5+ tests for double switch validation (optional - can defer)
- [ ] All tests passing
- [ ] Edge cases covered (player already out, not in roster, already active, etc.)
--- ---
## 🚀 Quick Start for Next Session ### Task 3: Substitution Manager Integration Tests (2 hours)
### To continue Phase 3E-Final: **File(s)**: `backend/tests/integration/test_substitution_manager.py` (NEW)
1. **Start with WebSocket handlers** (highest user impact): **Goal**: Integration tests for SubstitutionManager that verify full DB + state sync flow.
```bash
cd /mnt/NV2/Development/strat-gameplay-webapp/backend
source venv/bin/activate
```
2. **Test current implementation**: **Changes**:
```bash
export PYTHONPATH=.
python test_pd_api_live.py # Verify API still works
```
3. **Add Redis** (requires Redis server running): Create new integration test file:
```bash
# Install Redis if needed
sudo dnf install redis # or brew install redis
# Start Redis ```python
sudo systemctl start redis """
Integration tests for SubstitutionManager.
# Add to requirements Tests full flow: validation → DB → state sync
echo "redis>=5.0.0" >> requirements.txt """
pip install redis import pytest
``` from uuid import uuid4
from app.core.substitution_manager import SubstitutionManager
from app.core.state_manager import state_manager
from app.database.operations import DatabaseOperations
from app.models.game_models import GameState
4. **Run existing tests**: @pytest.fixture
```bash async def setup_game():
pytest tests/integration/test_position_ratings_api.py -v """Create test game with lineups in database."""
``` db_ops = DatabaseOperations()
game_id = uuid4()
# Create game in DB
await db_ops.create_game(
game_id=game_id,
league_id='sba',
home_team_id=1,
away_team_id=2,
game_mode='friendly',
visibility='public'
)
# Create lineup entries in DB
lineup_ids = {}
for i in range(1, 10):
lineup_id = await db_ops.add_sba_lineup_player(
game_id=game_id,
team_id=1,
player_id=100 + i,
position='P' if i == 1 else f'{i}B',
batting_order=i,
is_starter=True
)
lineup_ids[i] = lineup_id
# Add bench players
bench_ids = {}
for i in range(1, 4):
bench_id = await db_ops.add_sba_lineup_player(
game_id=game_id,
team_id=1,
player_id=200 + i,
position='OF',
batting_order=None,
is_starter=False
)
bench_ids[i] = bench_id
# Create game state
state = await state_manager.create_game(
game_id=game_id,
league_id='sba',
home_team_id=1,
away_team_id=2
)
state.current_batter_lineup_id = lineup_ids[1]
state_manager.update_state(game_id, state)
yield game_id, lineup_ids, bench_ids
# Cleanup
state_manager.remove_game(game_id)
@pytest.mark.asyncio
async def test_pinch_hit_full_flow(setup_game):
"""Test complete pinch hit flow: validation → DB → state."""
game_id, lineup_ids, bench_ids = await setup_game
db_ops = DatabaseOperations()
manager = SubstitutionManager(db_ops)
# Execute substitution
result = await manager.pinch_hit(
game_id=game_id,
player_out_lineup_id=lineup_ids[1],
player_in_card_id=201,
team_id=1
)
# Verify result
assert result.success
assert result.new_lineup_id is not None
assert result.player_out_lineup_id == lineup_ids[1]
# Verify database updated
lineup = await db_ops.get_active_lineup(game_id, team_id=1)
active_ids = [p.id for p in lineup]
assert result.new_lineup_id in active_ids
assert lineup_ids[1] not in active_ids
# Verify state updated
state = state_manager.get_state(game_id)
assert state.current_batter_lineup_id == result.new_lineup_id
# Verify lineup cache updated
roster = state_manager.get_lineup(game_id, team_id=1)
old_player = roster.get_player_by_lineup_id(lineup_ids[1])
assert not old_player.is_active
new_player = roster.get_player_by_lineup_id(result.new_lineup_id)
assert new_player.is_active
assert new_player.card_id == 201
# Add similar tests for defensive_replace and change_pitcher
```
**Files to Create**:
- `backend/tests/integration/test_substitution_manager.py` (new file, ~400 lines)
**Test Command**:
```bash
cd backend
pytest tests/integration/test_substitution_manager.py -v
```
**Acceptance Criteria**:
- [ ] Test pinch_hit full flow (DB + state sync verified)
- [ ] Test defensive_replace full flow
- [ ] Test change_pitcher full flow
- [ ] Test validation failures (error handling)
- [ ] Test state recovery after substitution
- [ ] All tests passing
--- ---
## 📝 Important Notes ### Task 4: Documentation - API Reference (1 hour)
### Architecture Decisions **File(s)**: `.claude/implementation/SUBSTITUTION_API_REFERENCE.md` (NEW)
- **In-memory cache is temporary**: Intentional technical debt, Redis upgrade planned
- **StateManager pattern**: O(1) defender lookups, no DB queries during play resolution
- **League-agnostic design**: Config-driven behavior, easy to extend to new leagues
- **Graceful degradation**: Always works even if API/cache is down
### API Details **Goal**: Create comprehensive API documentation for substitution system usage.
- **Base URL**: `https://pd.manticorum.com`
- **Endpoint**: `GET /api/v2/cardpositions?player_id={id}&position={pos}`
- **Rate Limiting**: Unknown - caching mitigates this risk
- **Error Handling**: Returns empty list on API errors, uses defaults
### Testing Strategy **Changes**:
- **Live tests**: Use player 8807 (7 positions) for comprehensive testing
- **Mock tests**: For CI/CD without API dependency
- **Integration tests**: Full flow from API → GameState → X-Check
### Performance Create new documentation file with:
- Cache is critical: 16,000x+ speedup
- Load ratings at game start (not during play resolution) 1. **WebSocket Events Reference**:
- Redis will enable cross-instance sharing and persistence - Event names
- Request data format
- Response data format
- Error codes
- Example payloads
2. **Python API Reference**:
- SubstitutionManager methods
- SubstitutionRules methods
- Database operations
- Example code snippets
3. **Workflows**:
- Pinch hitter flow diagram
- Defensive replacement flow
- Pitching change flow
- Error handling patterns
4. **Integration Guide**:
- How to integrate with GameEngine
- How to handle substitutions in UI
- State management considerations
**Files to Create**:
- `.claude/implementation/SUBSTITUTION_API_REFERENCE.md` (~300 lines)
**Acceptance Criteria**:
- [ ] All WebSocket events documented with examples
- [ ] All Python APIs documented
- [ ] Error codes reference table included
- [ ] Flow diagrams for each substitution type
- [ ] Integration examples provided
--- ---
## 🔗 Related Documentation ## Files to Review Before Starting
- **Full implementation details**: `backend/CLAUDE.md` lines 2132-2264 **Critical files** (understand before coding):
- **Phase 3E-Main commit**: `02e816a`
- **PD API client**: `app/services/pd_api_client.py` 1. **`backend/app/core/substitution_rules.py`** - All validation logic
- **Position rating service**: `app/services/position_rating_service.py` - Understand each validation method and error codes
- **Live integration test**: `test_pd_api_live.py`
2. **`backend/app/core/substitution_manager.py`** - Orchestration logic
- Understand DB-first pattern implementation
- Note how state is updated after DB
3. **`backend/app/database/operations.py:290-403`** - DB operations
- Review `create_substitution()` method
- Note transaction handling
4. **`backend/app/models/game_models.py:128-141`** - Model helper
- Review `get_player_by_card_id()` method
5. **`backend/app/websocket/handlers.py`** - Existing WebSocket patterns
- Understand error handling pattern
- Note broadcast vs. emit_to_user patterns
6. **`.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md`** - Architecture overview
- Complete context on design decisions
7. **`backend/app/core/game_engine.py:33-42`** - GameEngine initialization
- See where SubstitutionManager will be integrated
--- ---
**Last Updated**: 2025-11-03 ## Verification Steps
**Next Session Focus**: WebSocket handlers + Redis caching (Phase 3E-Final)
After completing all tasks:
### 1. Run all tests:
```bash
cd backend
source venv/bin/activate
# Unit tests
pytest tests/unit/core/test_substitution_rules.py -v
# Integration tests
pytest tests/integration/test_substitution_manager.py -v
# All tests
pytest -v
```
### 2. Manual testing with terminal client:
```bash
python -m terminal_client
# In REPL:
⚾ > new_game
⚾ > status # Note current batter lineup_id
⚾ > # Manual WebSocket test would go here (needs WebSocket client)
```
### 3. Verify database state:
```sql
-- Check lineup entries
SELECT id, card_id, position, batting_order, is_active, is_starter, entered_inning, replacing_id
FROM lineups
WHERE game_id = '<test_game_id>'
ORDER BY id;
-- Verify inactive players
SELECT * FROM lineups WHERE is_active = false;
```
### 4. Commit changes:
```bash
git add backend/app/websocket/handlers.py
git add backend/tests/unit/core/test_substitution_rules.py
git add backend/tests/integration/test_substitution_manager.py
git add .claude/implementation/SUBSTITUTION_API_REFERENCE.md
git commit -m "CLAUDE: Phase 3 - Complete Substitution System with WebSocket & Tests
- Add WebSocket event handlers for substitutions
- Comprehensive unit tests for validation rules (37+ tests)
- Integration tests for full DB + state flow (8+ tests)
- Complete API documentation with examples
Substitution system now 100% complete and production-ready.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
```
---
## Success Criteria
**Phase 3 Substitution System** will be **100% complete** when:
- [ ] 4 WebSocket event handlers implemented and tested
- [ ] 37+ unit tests passing for SubstitutionRules
- [ ] 8+ integration tests passing for SubstitutionManager
- [ ] API documentation complete with examples
- [ ] Manual testing successful via terminal client
- [ ] Database verification confirms audit trail working
- [ ] Git commit created
**Overall Phase 3 Progress** will be:
- Phase 3E (X-Check): 100% complete ✅
- Phase 3F (Substitutions): 100% complete ✅ (after this session)
- **Phase 3 Overall: ~99% complete** (only minor TODOs deferred to Phase 4+)
---
## Quick Reference
**Current Test Count**: 327 tests (base), +45 expected after this session
**Last Test Run**: All passing (2025-11-03)
**Branch**: `implement-phase-3`
**Python**: 3.13.3
**Virtual Env**: `backend/venv/`
**Key Imports for Next Session**:
```python
# WebSocket integration
from app.core.substitution_manager import SubstitutionManager
from app.core.state_manager import state_manager
from uuid import UUID
# Testing
import pytest
from app.core.substitution_rules import SubstitutionRules, ValidationResult
from app.models.game_models import GameState, LineupPlayerState, TeamLineupState
```
**Recent Commit History** (Last 10):
```
d1619b4 - CLAUDE: Phase 3 - Substitution System Core Logic (2 minutes ago)
adf7c76 - CLAUDE: Phase 3E-Final - Redis Caching & X-Check WebSocket Integration (66 minutes ago)
7d15018 - CLAUDE: Update documentation for Phase 3E-Main completion (2 hours ago)
02e816a - CLAUDE: Phase 3E-Main - Position Ratings Integration for X-Check Resolution (3 hours ago)
a55b31d - CLAUDE: Update documentation for Phase 3E-Prep completion (10 hours ago)
d560844 - CLAUDE: Phase 3E-Prep - Refactor GameState to use full LineupPlayerState objects (10 hours ago)
7417a3f - Offline catchup (11 hours ago)
683954f - CLAUDE: Update implementation notes to reflect Phase 2 completion (17 hours ago)
fc0e2f1 - CLAUDE: Integrate X-Check advancement with full GameState support (24 hours ago)
5f42576 - CLAUDE: Remove double-dipping on double play probability (24 hours ago)
```
---
## Context for AI Agent Resume
**If the next agent needs to understand the bigger picture**:
- Overall project: See `@prd-web-scorecard-1.1.md` and `@CLAUDE.md`
- Architecture: See `@.claude/implementation/00-index.md`
- Current phase details: See `@.claude/implementation/03-gameplay-features.md`
- Substitution system: See `@.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md`
**Critical files in current focus area**:
1. `backend/app/core/substitution_rules.py` - All validation logic (345 lines)
2. `backend/app/core/substitution_manager.py` - Orchestration (552 lines)
3. `backend/app/database/operations.py` - DB operations (with substitution methods)
4. `backend/app/websocket/handlers.py` - Will add event handlers here
5. `backend/app/core/game_engine.py` - GameEngine class
6. `backend/app/core/state_manager.py` - StateManager singleton
7. `backend/app/models/game_models.py` - Game state models
8. `backend/app/models/db_models.py` - Lineup model with substitution fields
9. `.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md` - Architecture doc
10. `backend/tests/unit/core/` - Where to add unit tests
**What NOT to do**:
- Don't modify core validation logic - it's complete and follows baseball rules correctly
- Don't change DB-first pattern - it's critical for data integrity
- Don't add position eligibility validation yet - deferred to post-MVP
- Don't implement double switch WebSocket events - deferred to post-MVP
- Don't change database schema - all fields needed are already there
**Patterns we've established**:
- DB-first: validate → DB → state → broadcast
- Error codes for all validation failures
- Comprehensive logging at every step
- Immutable lineup history (never delete, use is_active flag)
- Batting order inheritance for substitutions
---
**Estimated Time for Next Session**: 6-7 hours (2-3 WebSocket + 2 unit tests + 2 integration + 1 docs)
**Priority**: Medium (completes substitution system, not blocking other work)
**Blocking Other Work**: No (X-Check and core game engine are complete)
**Next Milestone After This**: Phase 4 - Spectator Mode & Polish OR Phase 3 - AI Opponent (choose based on priority)