strat-gameplay-webapp/.claude/status-2025-10-22-0113.md
Cal Corum a287784328 CLAUDE: Complete Week 4 - State Management & Persistence
Implemented hybrid state management system with in-memory game states and async
PostgreSQL persistence. This provides the foundation for fast gameplay (<500ms
response) with complete state recovery capabilities.

## Components Implemented

### Production Code (3 files, 1,150 lines)
- app/models/game_models.py (492 lines)
  - Pydantic GameState with 20+ helper methods
  - RunnerState, LineupPlayerState, TeamLineupState
  - DefensiveDecision and OffensiveDecision models
  - Full Pydantic v2 validation with field validators

- app/core/state_manager.py (296 lines)
  - In-memory state management with O(1) lookups
  - State recovery from database
  - Idle game eviction mechanism
  - Statistics tracking

- app/database/operations.py (362 lines)
  - Async PostgreSQL operations
  - Game, lineup, and play persistence
  - Complete state loading for recovery
  - GameSession WebSocket state tracking

### Tests (4 files, 1,963 lines, 115 tests)
- tests/unit/models/test_game_models.py (60 tests, ALL PASSING)
- tests/unit/core/test_state_manager.py (26 tests, ALL PASSING)
- tests/integration/database/test_operations.py (21 tests)
- tests/integration/test_state_persistence.py (8 tests)
- pytest.ini (async test configuration)

### Documentation (6 files)
- backend/CLAUDE.md (updated with Week 4 patterns)
- .claude/implementation/02-week4-state-management.md (marked complete)
- .claude/status-2025-10-22-0113.md (planning session summary)
- .claude/status-2025-10-22-1147.md (implementation session summary)
- .claude/implementation/player-data-catalog.md (player data reference)
- Week 5 & 6 plans created

## Key Features

- Hybrid state: in-memory (fast) + PostgreSQL (persistent)
- O(1) state access via dictionary lookups
- Async database writes (non-blocking)
- Complete state recovery from database
- Pydantic validation on all models
- Helper methods for common game operations
- Idle game eviction with configurable timeout
- 86 unit tests passing (100%)

## Performance

- State access: O(1) via UUID lookup
- Memory per game: ~1KB (just state)
- Target response time: <500ms 
- Database writes: <100ms (async) 

## Testing

- Unit tests: 86/86 passing (100%)
- Integration tests: 29 written
- Test configuration: pytest.ini created
- Fixed Pydantic v2 config deprecation
- Fixed pytest-asyncio configuration

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 12:01:03 -05:00

20 KiB
Raw Blame History

Session Summary: Phase 2 Game Engine Planning

Date: 2025-10-22 Time: ~23:00 - 01:13 Duration: ~2 hours Branch: implement-phase-2 Status: Planning Complete - Ready to Begin Implementation


Session Overview

Primary Objectives

  1. Plan Phase 2: Game Engine Core implementation (Weeks 4-6)
  2. Design in-memory state management architecture
  3. Document all player data fields for caching
  4. Create detailed weekly implementation plans

Technologies Involved

  • Backend: FastAPI, Python 3.13, Pydantic, SQLAlchemy (async)
  • Database: PostgreSQL (10.10.0.42:5432)
  • State Management: In-memory Python dictionaries + async DB persistence
  • Testing: pytest, automated Python tests

Overall Outcome

Successfully completed comprehensive planning for Phase 2

  • Created 4 detailed planning documents
  • Resolved key architectural questions about state caching
  • Documented complete player data model from Discord bot
  • Established "measure twice, cut once" approach
  • Ready to begin Week 4 implementation

Current State

Active Todos (from TodoWrite)

Completed:

  1. Create detailed Phase 2 implementation plan documents
  2. Document all player data fields to cache in memory

In Progress: 3. 🔄 Review player data catalog and supplement with any missing fields

Pending (Week 4): 4. Week 4: Create Pydantic game state models with rich player cache 5. Week 4: Implement State Manager (in-memory game state) 6. Week 4: Implement database operations layer 7. Week 4: Build state recovery mechanism

Pending (Week 5): 8. Week 5: Implement cryptographic dice system 9. Week 5: Build play resolver (simplified charts) 10. Week 5: Create game engine core logic 11. Week 5: Implement rule validators

Pending (Week 6): 12. Week 6: Create polymorphic player model architecture 13. Week 6: Implement league configuration system 14. Week 6: Build result charts (d20 tables) 15. Week 6: Create league API client 16. Week 6: End-to-end testing of complete at-bat flow

Running Services

  • Backend FastAPI: Multiple instances running on port 8000 (multiple background processes)
  • Frontend SBA: Running on port 3000
  • Frontend PD: Running on port 3001

Git Status

Branch: implement-phase-2
Modified: .claude/implementation/02-game-engine.md
Recent commits:
  d8a43fa - CLAUDE: Complete Phase 1 - Frontend Infrastructure Setup
  fc7f53a - CLAUDE: Complete Phase 1 backend infrastructure setup
  5c75b93 - CLAUDE: Initial project setup - documentation and infrastructure

Key Files Being Worked On

  • .claude/implementation/02-game-engine.md (updated with approach)
  • .claude/implementation/02-week4-state-management.md (created)
  • .claude/implementation/02-week5-game-logic.md (created)
  • .claude/implementation/02-week6-league-features.md (created)
  • .claude/implementation/player-data-catalog.md (created)

Changes Made

Files Created

  1. .claude/implementation/02-week4-state-management.md

    • Comprehensive Week 4 implementation plan
    • Pydantic game state models specification
    • StateManager class design
    • DatabaseOperations async layer
    • State recovery mechanism
    • Complete test specifications
  2. .claude/implementation/02-week5-game-logic.md

    • Week 5 implementation plan
    • DiceSystem with cryptographic RNG
    • PlayResolver with simplified charts
    • GameEngine orchestration
    • Rule validators
    • Integration test framework
  3. .claude/implementation/02-week6-league-features.md

    • Week 6 implementation plan
    • Polymorphic player models (BasePlayer → SbaPlayer/PdPlayer)
    • League configuration system
    • Result charts for SBA and PD
    • LeagueApiClient implementation
    • End-to-end testing strategy
  4. .claude/implementation/player-data-catalog.md

    • CRITICAL REFERENCE: Complete player data field specifications
    • Batting card data (8 basic fields + 54 rating values per player)
    • Pitching card data (7 basic fields + 60 rating values per player)
    • Defensive ratings (5 fields per position, multi-position support)
    • Memory usage analysis (~500-700 bytes per player)
    • Usage examples for gameplay scenarios
    • Data loading and caching strategies

Files Modified

  1. .claude/implementation/02-game-engine.md (lines 171-209)
    • Added "Implementation Approach" section
    • Documented key decisions from user
    • Added links to detailed weekly plans
    • Updated status to "In Progress - Planning Complete"

No Code Files Created/Modified

This session was planning only - no implementation code written yet.


Key Decisions & Discoveries

Architectural Decision: Rich In-Memory Caching

Decision: Cache complete player objects with ALL ratings in memory, not minimal state.

Rationale (from Discord bot experience):

  • Gameplay requires frequent access to:
    • Player images for display
    • Defensive ratings for x-checks (unpredictable position)
    • Catcher passed ball / pitcher wild pitch for chaos rolls
    • Stealing ratings, bunting ratings, etc.
  • Memory cost is negligible: ~10-15KB per game (20 players × ~500-700 bytes)
  • 100 concurrent games = < 2MB total
  • Discord bot suffered from slow DB queries - this solves that

Pattern Established:

class CachedPlayer(BaseModel):
    # Complete player data cached
    - Identity & display (12 fields)
    - Batting attributes (8 fields)
    - Batting ratings vL and vR (54 values total)
    - Pitching attributes (7 fields)
    - Pitching ratings vL and vR (60 values total)
    - Defense ratings per position (Dict[str, DefenseRatings])

# Cache in GameState
home_lineup: Dict[int, CachedPlayer]  # {lineup_id: player}
away_lineup: Dict[int, CachedPlayer]

Decision: SBA First, PD Second

Approach: Build each component for SBA league first, learn lessons, apply to PD.

Rationale:

  • SBA is simpler (fewer fields, manual result selection)
  • PD adds complexity (auto-selection via scouting model)
  • Ensures base case works before adding complexity
  • Matches user's request

Decision: Automated Python Testing (No WebSocket UI Tests in Phase 2)

Approach: Test via Python scripts and unit/integration tests, not through UI.

Benefits:

  • Faster iteration during development
  • Easier to debug game logic
  • Can test edge cases more thoroughly
  • UI testing comes in Phase 3

Test Script Pattern:

# scripts/test_game_flow.py
async def test_at_bat():
    state = await state_manager.create_game(...)
    await game_engine.start_game(game_id)
    await game_engine.submit_defensive_decision(...)
    await game_engine.submit_offensive_decision(...)
    result = await game_engine.resolve_play(game_id)

Decision: Hybrid State Management

Pattern:

User Action → WebSocket → Game Engine
                ↓
         Update In-Memory State (fast, <200ms)
                ↓
         Async Write to PostgreSQL (non-blocking, <100ms)
                ↓
         Broadcast via WebSocket

Data Consistency Strategy:

  • In-memory state is source of truth for active games
  • Database is async backup + historical record
  • On crash: Recover from DB plays, rebuild in-memory cache
  • Write-through cache pattern

Discovery: Discord Bot Data Model Complexity

Finding: Paper Dynasty Discord bot has extensive player data:

  • Batting: 27 rating fields × 2 platoon splits (vs LHP/RHP) = 54 values
  • Pitching: 30 rating fields × 2 platoon splits (vs LHB/RHB) = 60 values
  • Defense: Multi-position support (player can have ratings for 2-8 positions)
  • Chaos Events: Wild pitch, passed ball, balk, pickoff ratings
  • X-Checks: 9 position-specific x-check probabilities on pitcher cards

Implication: Must cache ALL this data for fast gameplay. Initial "minimal state" approach would have failed.

Pattern: Result Selection Models

SBA League:

  • Players see dice roll FIRST
  • Select from available results on chart
  • Manual decision required

PD League:

  • Flexible approach
  • Manual selection OR auto-resolution via scouting model
  • Scouting model uses detailed BattingCardRatings/PitchingCardRatings

Problems & Solutions

Problem: Initial Architecture Too Simple

Issue: Original plan had minimal in-memory state (just current batter ID, outs, score).

Discovery: User explained real gameplay needs:

  • "On each play there is a chance for a chaos roll if there are baserunners - need catcher passed_ball and pitcher wild_pitch"
  • "X-check plays require calling .range and .error values"
  • "Need to display player images for batter, runners, pitcher, catcher"

Solution: Switched to rich player caching with complete data model.

Lesson: Real-world production experience (Discord bot) revealed requirements that weren't obvious from specs.

Problem: Unclear Data Requirements

Issue: Didn't know all the fields that needed to be cached.

Solution: Deep dive into Discord bot codebase:

  • Read paper-dynasty/database/app/routers_v2/battingcards.py
  • Read paper-dynasty/database/app/routers_v2/pitchingcards.py
  • Read paper-dynasty/database/app/routers_v2/battingcardratings.py
  • Read paper-dynasty/database/app/routers_v2/pitchingcardratings.py
  • Read paper-dynasty/database/app/routers_v2/cardpositions.py
  • Read paper-dynasty/database/app/routers_v2/players.py

Result: Created comprehensive player-data-catalog.md documenting all 100+ fields.

Problem: Performance vs Consistency Trade-off

Issue: How to balance fast gameplay with data consistency?

Solution: Async write-through cache

  • In-memory cache updated synchronously (fast)
  • Database write happens asynchronously (non-blocking)
  • On crash, rebuild from database plays

Guarantees:

  • Response time < 500ms (in-memory reads)
  • Data persisted within seconds (async writes)
  • Full recovery possible from database

Technology Context

Database Server

  • Location: 10.10.0.42:5432
  • Database: paperdynasty_dev
  • User: paperdynasty
  • Connection: postgresql+asyncpg://paperdynasty:PASSWORD@10.10.0.42:5432/paperdynasty_dev

Python Environment

  • Version: Python 3.13.3
  • Virtual Env: backend/venv/
  • Activation: source venv/bin/activate (from backend directory)

Critical Dependencies

  • Pydantic: v2.10.6 (data validation)
  • SQLAlchemy: v2.0.36 (async ORM)
  • asyncpg: v0.30.0 (PostgreSQL async driver)
  • Pendulum: v3.0.0 (datetime - ALWAYS use instead of Python datetime)
  • greenlet: Required for SQLAlchemy async

League API References

  • SBA API: Integration pending (Week 6)
  • PD API: Discord bot at /mnt/NV2/Development/paper-dynasty/database/

Database Models (Phase 1 Complete)

  • Game: UUID primary key, AI support, team tracking
  • Play: 25+ statistics fields, player FKs, on_base_code bit field
  • Lineup: Substitution tracking, fatigue flags
  • GameCardsetLink: PD cardset validation
  • RosterLink: PD roster management
  • GameSession: WebSocket state tracking

Next Steps

Immediate Actions (User Review Required)

  1. Review Player Data Catalog (.claude/implementation/player-data-catalog.md)

    • Check for missing fields
    • Verify field types
    • Confirm usage scenarios
    • Answer questions at end of document
  2. Answer Architecture Questions:

    • Variant Cards: Are these different versions of same player in same game?
    • Offensive Column: What is offense_col used for?
    • SBA Simplifications: Cache ratings or truly use simplified charts?
    • Scouting Data: Is BattingCardRatings the scouting data or separate?

Once Review Complete → Begin Week 4

First Implementation Task: Create Pydantic game state models

  • Location: backend/app/models/game_models.py
  • Models needed:
    • CachedPlayer (complete player with all ratings)
    • BattingAttributes, BattingRatings
    • PitchingAttributes, PitchingRatings
    • DefensivePosition
    • GameState (with rich lineup caches)
    • RunnerState, decision models

Test First: Write unit tests before implementation

  • tests/unit/models/test_game_models.py
  • Test model validation
  • Test helper methods
  • Test platoon split lookups

Week 4 Complete Deliverables

  • Pydantic models with full player data
  • StateManager with in-memory game states
  • DatabaseOperations for async persistence
  • State recovery from database
  • All tests passing

Week 5-6 (After Week 4)

Follow plans in:

  • .claude/implementation/02-week5-game-logic.md
  • .claude/implementation/02-week6-league-features.md

Reference Information

Critical Planning Documents

  1. .claude/implementation/02-game-engine.md

    • Phase 2 overview
    • Implementation approach and decisions
    • Links to weekly plans
  2. .claude/implementation/02-week4-state-management.md

    • Complete Week 4 plan with code examples
    • Pydantic models specification
    • StateManager design
    • Database operations layer
    • Testing strategy
  3. .claude/implementation/02-week5-game-logic.md

    • Dice system (cryptographic d20 rolls)
    • Play resolver with result charts
    • Game engine orchestration
    • Rule validators
    • Integration tests
  4. .claude/implementation/02-week6-league-features.md

    • Polymorphic player models
    • League configurations (SBA vs PD)
    • Result charts
    • API client
    • E2E testing
  5. .claude/implementation/player-data-catalog.md MOST CRITICAL

    • Complete field specifications for caching
    • Memory usage analysis
    • Usage examples
    • Loading strategies
    • MUST READ before implementing models

Discord Bot Reference Locations

Player Data Models:

  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/players.py:35-62 - PlayerPydantic
  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/battingcards.py:22-33 - BattingCardModel
  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/pitchingcards.py:22-33 - PitchingCardModel
  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/cardpositions.py:22-39 - CardPositionModel
  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/battingcardratings.py:29-60 - BattingCardRatingsModel
  • /mnt/NV2/Development/paper-dynasty/database/app/routers_v2/pitchingcardratings.py:28-61 - PitchingCardRatingsModel

Existing Backend Structure

Database Models (Phase 1 Complete):

  • backend/app/models/db_models.py:34-70 - Game model
  • backend/app/models/db_models.py:72-205 - Play model (extensive stats)
  • backend/app/models/db_models.py:207-233 - Lineup model
  • backend/app/models/db_models.py:10-20 - GameCardsetLink
  • backend/app/models/db_models.py:22-31 - RosterLink
  • backend/app/models/db_models.py:235-246 - GameSession

Backend Infrastructure (Phase 1 Complete):

  • backend/app/main.py - FastAPI app with Socket.io
  • backend/app/config.py - Settings with Pydantic
  • backend/app/database/session.py - Async database session
  • backend/app/websocket/connection_manager.py - WebSocket lifecycle
  • backend/app/websocket/handlers.py - Socket.io event handlers
  • backend/app/utils/logging.py - Rotating logger setup

Empty Directories Ready for Phase 2:

  • backend/app/core/ - Game engine, state manager, play resolver
  • backend/app/config/ - League configs (currently just app config)
  • backend/app/data/ - API client

Important Patterns from CLAUDE.md

DateTime Handling - ALWAYS use Pendulum:

import pendulum
now = pendulum.now('UTC')  # ✅ Correct
formatted = now.format('YYYY-MM-DD HH:mm:ss')

# ❌ NEVER use:
from datetime import datetime

Logging Pattern:

import logging
logger = logging.getLogger(f'{__name__}.ClassName')
logger.info(f"Message with context: {variable}")

Error Handling - "Raise or Return" pattern:

# ✅ Raise exceptions for errors
def get_player(player_id: int) -> Player:
    player = find_player(player_id)
    if not player:
        raise ValueError(f"Player {player_id} not found")
    return player

# ❌ Don't return Optional unless specifically required
def get_player(player_id: int) -> Optional[Player]:  # Avoid this

Git Commits - Prefix with "CLAUDE: ":

git commit -m "CLAUDE: Implement Week 4 state manager"

Common Operations

Start Backend:

cd /mnt/NV2/Development/strat-gameplay-webapp/backend
source venv/bin/activate
python -m app.main
# Available at http://localhost:8000
# API docs at http://localhost:8000/docs

Run Tests:

cd /mnt/NV2/Development/strat-gameplay-webapp/backend
source venv/bin/activate
pytest tests/ -v
pytest tests/unit/models/test_game_models.py -v  # Specific test

Database Connection Test:

psql postgresql://paperdynasty:PASSWORD@10.10.0.42:5432/paperdynasty_dev

Performance Targets (Phase 2)

Critical Metrics:

  • Action response: < 500ms (user action → state update)
  • WebSocket delivery: < 200ms
  • Database write: < 100ms (async, non-blocking)
  • State recovery: < 2 seconds (rebuild from DB)
  • Concurrent games: 10+ simultaneous active games
  • Memory per game: ~10-15KB (20 cached players)

How We'll Achieve Them:

  • In-memory state (no DB queries during plays)
  • Async database writes (non-blocking)
  • Lightweight Pydantic models (fast serialization)
  • Efficient state recovery (single query with joins)

Architecture Summary

The "Two-Model" Pattern

In-Memory (Pydantic):

class GameState(BaseModel):
    # Fast, lightweight, optimized for game logic
    game_id: UUID
    inning: int
    outs: int
    home_score: int
    away_score: int
    home_lineup: Dict[int, CachedPlayer]  # Complete player data
    away_lineup: Dict[int, CachedPlayer]
    runners: List[RunnerState]
    current_batter_id: int
    # ...

Database (SQLAlchemy):

class Game(Base):
    # Persistent, complete, auditable
    id = Column(UUID, primary_key=True)
    league_id = Column(String)
    current_inning = Column(Integer)
    home_score = Column(Integer)
    # Relationships
    plays = relationship("Play", cascade="all, delete-orphan")
    lineups = relationship("Lineup", cascade="all, delete-orphan")
    # ...

Why Both?:

  • Pydantic: Fast reads, easy WebSocket serialization, optimized structure
  • SQLAlchemy: Persistence, relationships, audit trail, crash recovery

Translation Layer: StateManager handles conversion between models


Questions for User (Awaiting Answers)

  1. Variant Cards: I saw variant: int = 0 in batting/pitching card models. Are variants different versions of the same player that could be in the same game simultaneously? Or are they alternate ratings for different seasons?

  2. Offensive Column: What is offense_col (BattingCard, PitchingCard) used for? Is this for result chart lookups or something else?

  3. SBA Simplifications: For SBA league, should we:

    • Option A: Still cache BattingCardRatings/PitchingCardRatings but use simplified result selection
    • Option B: Truly simplify and not cache detailed ratings at all
  4. Scouting Data: The PRD mentions "scouting data" for PD. Is the detailed BattingCardRatings / PitchingCardRatings (with all the probability fields) THE scouting data, or is there additional scouting information beyond those rating tables?

  5. Missing Fields: Are there any player attributes or ratings used in gameplay that aren't captured in the player-data-catalog.md document?


Success Criteria - Phase 2 Complete

By end of Phase 2, we will have:

  • Comprehensive planning documents (THIS SESSION)
  • In-memory game state management working
  • Play resolution engine with dice rolls
  • League configuration system (SBA and PD configs)
  • Polymorphic player models (BasePlayer, SbaPlayer, PdPlayer)
  • Database persistence layer with async operations
  • State recovery mechanism from database
  • Basic game flow (start → plays → end)
  • Complete ONE at-bat for SBA league
  • Complete ONE at-bat for PD league
  • All unit tests passing (90%+ coverage)
  • All integration tests passing
  • Dice distribution verified as uniform
  • Performance targets met (<500ms response)

Final Status

Planning Phase: COMPLETE

Ready to Begin: Week 4 - State Management & Persistence

Blockers: None - awaiting user review of player-data-catalog.md

Confidence Level: High - comprehensive planning with proven Discord bot reference

Next Session: Implement Pydantic game state models after user review


Session saved: 2025-10-22 01:13 Document: .claude/status-2025-10-22-0113.md