strat-gameplay-webapp/backend/app/models/visual_model_relationships.md
Cal Corum 1c32787195 CLAUDE: Refactor game models and modularize terminal client
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>
2025-10-28 14:16:38 -05:00

20 KiB

════════════════════════════════════════════════════════════════════════════════ BACKEND MODEL RELATIONSHIPS ════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────┐ │ DATABASE MODELS (db_models.py) │ │ SQLAlchemy ORM - PostgreSQL Tables │ └─────────────────────────────────────────────────────────────────────────────┘

                                ┌─────────────┐
                                │    GAME     │ (games table)
                                │ PK: id(UUID)│
                                │             │
                                │ league_id   │ ('sba' or 'pd')
                                │ home_team_id│
                                │ away_team_id│
                                │ status      │
                                │ game_mode   │
                                │ inning/half │
                                │ scores      │
                                │             │
                                │ AI Support: │
                                │ - home_is_ai│
                                │ - away_is_ai│
                                │ - difficulty│
                                └──────┬──────┘
                                       │
                  ┌────────────────────┼────────────────────┐
                  │                    │                    │
                  ▼                    ▼                    ▼
            ┌──────────┐         ┌──────────┐        ┌──────────┐
            │   PLAY   │         │  LINEUP  │        │ SESSION  │
            │ (plays)  │         │(lineups) │        │(sessions)│
            └──────────┘         └──────────┘        └──────────┘
                  │                    │                    │
                  │                    │              (WebSocket state)
                  │                    │
     ┌────────────┼────────────┐       │
     │            │            │       │
     ▼            ▼            ▼       ▼
┌────────┐  ┌────────┐  ┌────────┐ ┌────────┐
│ batter │  │pitcher │  │catcher │ │on_first│
│  (FK)  │  │  (FK)  │  │  (FK)  │ │  (FK)  │
└────────┘  └────────┘  └────────┘ └────────┘
     │            │            │       │
     └────────────┴────────────┴───────┘
                  │
            All FK to LINEUP


                  ┌──────────────────────────────┐
                  │    GAME RELATIONSHIPS        │
                  │                              │
                  │  ┌─────────────────┐         │
                  │  │ GameCardsetLink │ (PD)    │
                  │  │  - game_id (FK) │         │
                  │  │  - cardset_id   │         │
                  │  │  - priority     │         │
                  │  └─────────────────┘         │
                  │                              │
                  │  ┌─────────────────┐         │
                  │  │   RosterLink    │         │
                  │  │  - game_id (FK) │         │
                  │  │  - team_id      │         │
                  │  │  - card_id  🅿   │(PD)    │
                  │  │  - player_id 🆂  │(SBA)   │
                  │  │  XOR constraint │         │
                  │  └─────────────────┘         │
                  │                              │
                  │  ┌─────────────────┐         │
                  │  │      Roll       │         │
                  │  │  - roll_id (PK) │         │
                  │  │  - game_id (FK) │         │
                  │  │  - roll_type    │         │
                  │  │  - roll_data    │         │
                  │  │  - context      │         │
                  │  └─────────────────┘         │
                  └──────────────────────────────┘

═══════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────┐ │ IN-MEMORY STATE MODELS (game_models.py) │ │ Pydantic v2 - Fast Validation & Serialization │ └─────────────────────────────────────────────────────────────────────────────┘

                        ┌──────────────────┐
                        │   GameState      │ (Core in-memory state)
                        │                  │
                        │ game_id: UUID    │
                        │ league_id: str   │
                        │ home_team_id     │
                        │ away_team_id     │
                        │ home/away_is_ai  │
                        │                  │
                        │ Game State:      │
                        │ - status         │
                        │ - inning/half    │
                        │ - outs           │
                        │ - scores         │
                        │                  │
                        │ Current Play:    │
                        │ - batter_id      │
                        │ - pitcher_id     │
                        │ - catcher_id     │
                        │ - on_base_code   │
                        │                  │
                        │ Decision Track:  │
                        │ - pending_dec    │
                        │ - decisions_dict │
                        │                  │
                        │ Batters Index:   │
                        │ - away_idx (0-8) │
                        │ - home_idx (0-8) │
                        └────────┬─────────┘
                                 │
                   ┌─────────────┼─────────────┐
                   │             │             │
                   ▼             ▼             ▼
         ┌──────────────┐ ┌─────────────┐ ┌──────────────┐
         │ RunnerState  │ │DefDecision  │ │OffDecision   │
         │              │ │             │ │              │
         │ lineup_id    │ │ alignment   │ │ approach     │
         │ card_id      │ │ if_depth    │ │ steal_atts   │
         │ on_base(1-3) │ │ of_depth    │ │ hit_and_run  │
         └──────────────┘ │ hold_runners│ │ bunt_attempt │
                          └─────────────┘ └──────────────┘


                        ┌──────────────────┐
                        │ TeamLineupState  │
                        │                  │
                        │ team_id: int     │
                        │ players: List[   │
                        │   LineupPlayer   │
                        │   State          │
                        │ ]                │
                        │                  │
                        │ Methods:         │
                        │ - get_batting_   │
                        │   order()        │
                        │ - get_pitcher()  │
                        │ - get_batter()   │
                        └──────────────────┘
                                 │
                                 │ Contains list of
                                 ▼
                        ┌──────────────────┐
                        │LineupPlayerState │
                        │                  │
                        │ lineup_id: int   │
                        │ card_id: int     │
                        │ position: str    │
                        │ batting_order    │
                        │ is_active: bool  │
                        └──────────────────┘

═══════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────┐ │ ROSTER MODELS (roster_models.py) │ │ Pydantic - Type-safe Roster Operations │ └─────────────────────────────────────────────────────────────────────────────┘

                   ┌────────────────────────┐
                   │ BaseRosterLinkData     │ (Abstract)
                   │                        │
                   │ id: Optional[int]      │
                   │ game_id: UUID          │
                   │ team_id: int           │
                   │                        │
                   │ Abstract Methods:      │
                   │ - get_entity_id()      │
                   │ - get_entity_type()    │
                   └───────────┬────────────┘
                               │
                   ┌───────────┴───────────┐
                   │                       │
                   ▼                       ▼
       ┌──────────────────────┐ ┌──────────────────────┐
       │ PdRosterLinkData     │ │ SbaRosterLinkData    │
       │                      │ │                      │
       │ card_id: int    🅿   │ │ player_id: int   🆂  │
       │                      │ │                      │
       │ get_entity_id()      │ │ get_entity_id()      │
       │  → returns card_id   │ │  → returns player_id │
       │                      │ │                      │
       │ get_entity_type()    │ │ get_entity_type()    │
       │  → returns "card"    │ │  → returns "player"  │
       └──────────────────────┘ └──────────────────────┘


                   ┌────────────────────────┐
                   │  RosterLinkCreate      │ (Request model)
                   │                        │
                   │ game_id: UUID          │
                   │ team_id: int           │
                   │ card_id: Optional      │
                   │ player_id: Optional    │
                   │                        │
                   │ Validation:            │
                   │ - XOR check (exactly   │
                   │   one ID required)     │
                   │                        │
                   │ Methods:               │
                   │ - to_pd_data()         │
                   │ - to_sba_data()        │
                   └────────────────────────┘

═══════════════════════════════════════════════════════════════════════════════

                        KEY RELATIONSHIPS SUMMARY

  Database (PostgreSQL)          In-Memory (Pydantic)        Roster Types
  ═════════════════════         ═══════════════════         ════════════

  Game ──┬──> Play              GameState                   BaseRosterLinkData
         │      │                    │                           │
         │      └──> Lineup          ├──> RunnerState            ├─> PdRosterLinkData
         │           (batter,        │                           │
         │            pitcher,       ├──> DefensiveDecision      └─> SbaRosterLinkData
         │            catcher,       │
         │            runners)       └──> OffensiveDecision
         │
         ├──> Lineup              TeamLineupState
         │                            │
         ├──> GameCardsetLink         └──> LineupPlayerState
         │    (PD only)
         │
         ├──> RosterLink          RosterLinkCreate ─┬─> PdRosterLinkData
         │    - card_id (PD)                         │
         │    - player_id (SBA)                      └─> SbaRosterLinkData
         │
         ├──> GameSession
         │    (WebSocket state)
         │
         └──> Roll
              (dice history)

═══════════════════════════════════════════════════════════════════════════════

                      POLYMORPHIC PATTERN SUMMARY

  Both `Lineup` and `RosterLink` support PD and SBA leagues:

  ┌──────────────────────────────────────────────────────────┐
  │  Lineup (db_models.py)         RosterLink (db_models.py) │
  │  ══════════════════             ════════════════════     │
  │                                                           │
  │  card_id (nullable)             card_id (nullable)       │
  │  player_id (nullable)           player_id (nullable)     │
  │                                                           │
  │  CHECK: exactly one populated   CHECK: exactly one       │
  │  UNIQUE: (game_id, card_id)     UNIQUE: (game_id, card)  │
  │  UNIQUE: (game_id, player_id)   UNIQUE: (game_id, player)│
  │                                                           │
  │  🅿  PD:  card_id NOT NULL      🅿  PD:  card_id NOT NULL │
  │  🆂  SBA: player_id NOT NULL    🆂  SBA: player_id NOT    │
  └──────────────────────────────────────────────────────────┘

═══════════════════════════════════════════════════════════════════════════════

                          DATA FLOW EXAMPLE

  1. Create Game
     ═══════════
     Game (DB) ────────────────> GameState (Memory)
        │                              │
        ├─> RosterLink (DB)            │
        ├─> GameCardsetLink (PD)       │
        └─> GameSession (DB)           │

  2. Setup Lineup
     ════════════
     Lineup (DB) ───────────────> TeamLineupState (Memory)
        │                              │
        └─> (card_id or player_id)     └─> LineupPlayerState[]

  3. Execute Play
     ════════════
     GameState ──┬──> DefensiveDecision
                 ├──> OffensiveDecision
                 ├──> RunnerState[]
                 └──> (resolve play)
                         │
                         ▼
                     Play (DB) ──> Save outcome
                         │
                         ├─> batter_id (FK to Lineup)
                         ├─> pitcher_id (FK to Lineup)
                         ├─> on_first/second/third_id
                         └─> 25+ stat fields

  4. Dice Roll Audit
     ═══════════════
     Roll (DB) ──> Stores cryptographic roll history
        │
        ├─> roll_data (JSONB): complete roll
        ├─> context (JSONB): game situation
        └─> Used for recovery/analytics

═══════════════════════════════════════════════════════════════════════════════