Critical fixes in game_engine.py: - Fix silent error swallowing in _batch_save_inning_rolls (re-raise) - Add per-game asyncio.Lock for race condition prevention - Add _cleanup_game_resources() for memory leak prevention - All 739 tests passing Documentation refactoring: - Created CODE_REVIEW_GAME_ENGINE.md documenting 24 identified issues - Trimmed backend/app/core/CLAUDE.md from 1371 to 143 lines - Trimmed frontend-sba/CLAUDE.md from 696 to 110 lines - Created focused subdirectory CLAUDE.md files: - frontend-sba/components/CLAUDE.md (105 lines) - frontend-sba/composables/CLAUDE.md (79 lines) - frontend-sba/store/CLAUDE.md (116 lines) - frontend-sba/types/CLAUDE.md (95 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2.8 KiB
2.8 KiB
Types Directory
TypeScript definitions matching backend Pydantic models. Type safety between frontend and backend.
File Overview
| File | Contents |
|---|---|
game.ts |
GameState, PlayResult, decisions, outcomes |
player.ts |
SbaPlayer, Lineup, LineupPlayerState |
websocket.ts |
Socket event types (Client↔Server) |
api.ts |
REST API request/response types |
index.ts |
Re-exports all types |
Critical Type Mappings
Backend → Frontend
| Backend (Python) | Frontend (TypeScript) | Notes |
|---|---|---|
GameState |
GameState |
game.ts:61 |
LineupPlayerState |
LineupPlayerState |
game.ts:44 |
DefensiveDecision |
DefensiveDecision |
game.ts:124 |
OffensiveDecision |
OffensiveDecision |
game.ts:136 |
PlayResult |
PlayResult |
game.ts:217 |
Lineup |
Lineup |
index.ts |
Key Type Relationships
GameState
├── current_batter: LineupPlayerState ← Minimal, for wire transfer
├── current_pitcher: LineupPlayerState
├── on_first/second/third: LineupPlayerState | null
└── pending_defensive_decision: DefensiveDecision | null
Lineup (from lineup_data event)
├── lineup_id: number ← Matches LineupPlayerState.lineup_id
├── position: string
├── is_active: boolean
└── player: SbaPlayer ← Full player data
├── id: number
├── name: string
└── headshot: string
Why Two Player Types?
LineupPlayerState (in GameState):
- Sent on every state update (~10-50 times per game)
- Minimal: lineup_id, position, batting_order
- ~50 bytes per player
Lineup (from lineup_data):
- Sent once when joining game
- Full data including nested player with name, headshot
- ~500 bytes per player
Optimization: Send minimal data frequently, full data once.
Common Type Usage
Accessing Player Data
// GameState gives you LineupPlayerState
const batterState: LineupPlayerState = gameStore.currentBatter
// Need to lookup full Lineup for player details
const batterLineup: Lineup = gameStore.findPlayerInLineup(batterState.lineup_id)
const name: string = batterLineup.player.name
Decision Types
// Defensive (fielding team)
interface DefensiveDecision {
infield_depth: 'infield_in' | 'normal' | 'corners_in'
outfield_depth: 'normal' | 'shallow'
hold_runners: number[]
}
// Offensive (batting team)
interface OffensiveDecision {
action: 'swing_away' | 'steal' | 'hit_and_run' | 'sac_bunt' | 'squeeze_bunt'
steal_attempts: number[]
}
Type Maintenance
When backend Pydantic models change:
- Update corresponding frontend type
- Check all components using that type
- Run
npm run type-checkto catch mismatches