strat-gameplay-webapp/.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md
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

9.9 KiB

Substitution System Implementation - Phase 3 Week 8

Date: 2025-11-03 Status: Core Logic Complete (3/5 phases done) Estimated Time: 5-6 hours completed, 3-4 hours remaining

Overview

Implemented core substitution system for baseball gameplay following established DB-first pattern.

Components Implemented

1. SubstitutionRules (validation logic)

File: backend/app/core/substitution_rules.py (345 lines)

Comprehensive baseball substitution validation:

Classes:

  • ValidationResult: Data class for validation results
  • SubstitutionRules: Static validation methods

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

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)
  • Position validation (valid baseball positions)
  • Timing checks (with warnings for mid-inning changes)

2. SubstitutionManager (orchestration logic)

File: backend/app/core/substitution_manager.py (552 lines)

Orchestrates substitutions with DB-first pattern:

Classes:

  • SubstitutionResult: Data class for operation results
  • SubstitutionManager: Main orchestration class

Methods:

  • pinch_hit(): Execute pinch hitter substitution
  • defensive_replace(): Execute defensive replacement
  • change_pitcher(): Execute pitching change

Pattern (DB-First):

  1. Validate using in-memory state
  2. Update DATABASE FIRST
  3. Update in-memory state SECOND
  4. Return result (WebSocket broadcast in handler)

Key Features:

  • Comprehensive error handling
  • State + DB sync guaranteed
  • Automatic lineup cache updates
  • Current player references updated (batter/pitcher/catcher)
  • Logging at every step

3. Database Operations (persistence)

File: backend/app/database/operations.py (extended)

Added substitution-specific operations:

Methods Added:

  • create_substitution(): Creates substitution in database
    • Marks old player inactive
    • Creates new lineup entry with metadata
    • Returns new lineup_id
  • 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

4. Model Enhancements (helper methods)

File: backend/app/models/game_models.py (modified)

Added helper method to TeamLineupState:

  • get_player_by_card_id(): Find player by card/player ID

Architecture Decisions

1. DB-First Pattern

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 is source of truth, survives server restarts

2. Immutable Lineup History

  • Never delete lineup entries
  • Use is_active flag for current lineup
  • replacing_id creates complete audit trail

Benefits:

  • Can reconstruct any moment in game history
  • Rollback support
  • Complete substitution tracking

3. Position Flexibility (MVP)

  • Don't enforce strict position eligibility in MVP
  • Any player can play any position
  • Can add position validation post-MVP

Rationale: Simplifies MVP, users know their rosters

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

What's NOT Implemented Yet

5. WebSocket Events (Next: 2-3 hours)

Need to add:

  • request_pinch_hitter event handler
  • request_defensive_replacement event handler
  • request_pitching_change event handler
  • Broadcast events:
    • player_substituted: Notify all clients
    • lineup_updated: Send updated lineup

6. Testing (2-3 hours)

Need to write:

  • Unit tests for SubstitutionRules
  • Integration tests for SubstitutionManager
  • WebSocket event tests
  • End-to-end substitution flow tests

7. Documentation (1 hour)

Need to document:

  • API usage examples
  • WebSocket event formats
  • Substitution workflows
  • Error codes reference

Files Created/Modified

Created:

backend/app/core/substitution_rules.py        (345 lines)
backend/app/core/substitution_manager.py       (552 lines)
.claude/implementation/SUBSTITUTION_SYSTEM_SUMMARY.md

Modified:

backend/app/models/game_models.py              (+15 lines - helper method)
backend/app/database/operations.py             (+115 lines - DB operations)

Total: ~1,027 lines of new code

Integration Points

With Game Engine

from app.core.substitution_manager import SubstitutionManager
from app.database.operations import DatabaseOperations

# In GameEngine.__init__()
self.substitution_manager = SubstitutionManager(self.db_ops)

# Usage
result = await self.substitution_manager.pinch_hit(
    game_id=game_id,
    player_out_lineup_id=current_batter_lineup_id,
    player_in_card_id=123,
    team_id=1
)

With WebSocket (To be implemented)

# In handlers.py
@sio.event
async def request_pinch_hitter(sid, data):
    result = await game_engine.substitution_manager.pinch_hit(...)

    if result.success:
        # Broadcast to all clients
        await manager.broadcast_to_game(
            game_id,
            'player_substituted',
            {
                'type': 'pinch_hitter',
                'player_out': result.player_out_lineup_id,
                'player_in': result.player_in_card_id,
                'new_lineup_id': result.new_lineup_id,
                'updated_lineup': result.updated_lineup.model_dump()
            }
        )
    else:
        await sio.emit('substitution_error', {
            'error': result.error_message,
            'code': result.error_code
        }, room=sid)

Success Criteria

Completed :

  • Validation rules enforced
  • DB-first pattern followed
  • Database + state stay in sync
  • Comprehensive error handling
  • Audit trail (replacing_id, entered_inning, after_play)
  • Logging at every step

Remaining :

  • WebSocket events implemented
  • Real-time lineup updates broadcast
  • Unit tests written
  • Integration tests written
  • API documentation complete
  • Substitution history visible in UI

Testing Strategy

Unit Tests (SubstitutionRules)

def test_validate_pinch_hitter_not_current_batter()
def test_validate_pinch_hitter_player_already_out()
def test_validate_pinch_hitter_substitute_not_in_roster()
def test_validate_pinch_hitter_substitute_already_active()
def test_validate_pinch_hitter_success()
# Similar for defensive_replacement and pitching_change

Integration Tests (SubstitutionManager)

async def test_pinch_hit_full_flow()
async def test_defensive_replace_full_flow()
async def test_change_pitcher_full_flow()
async def test_substitution_updates_state_correctly()
async def test_substitution_survives_recovery()

WebSocket Tests

async def test_pinch_hitter_event()
async def test_substitution_broadcast()
async def test_substitution_error_handling()

Performance Notes

Expected Latency:

  • Validation: < 1ms (in-memory)
  • Database update: < 20ms (INSERT + UPDATE)
  • State update: < 1ms (in-memory)
  • Total: < 25ms for complete substitution

Memory Impact:

  • Minimal (one additional LineupPlayerState object)
  • Old player remains in memory (marked inactive)

Next Steps

  1. Add WebSocket Events (2-3 hours)

    • Event handlers in app/websocket/handlers.py
    • Integrate SubstitutionManager
    • Broadcast to all clients
  2. Write Tests (2-3 hours)

    • Unit tests for validation rules
    • Integration tests for manager
    • WebSocket event tests
  3. Documentation (1 hour)

    • API usage guide
    • Event format reference
    • Error codes list
    • Example workflows

Commit Message

CLAUDE: Phase 3 - Substitution System Core Logic

Implemented comprehensive substitution system with DB-first pattern:

## Core Components (897 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>

Status: 60% complete - Core logic done, WebSocket + tests remaining Next Session: Implement WebSocket events and testing