CLAUDE: Auto-start game when both lineups submitted

- Broadcast game_state_update in game_engine.start_game() so frontend
  receives the active state immediately
- Add auto-start safeguard to /lineup-status endpoint for stuck pending
  games (both in-memory and database code paths)
- Fix None handling in state_manager._rebuild_state_from_data() for
  pending games with null inning/half values

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-01-16 14:56:35 -06:00
parent 1f5e290d8b
commit 4cb8e3f6c4
3 changed files with 37 additions and 4 deletions

View File

@ -858,6 +858,16 @@ async def get_lineup_status(game_id: str):
for p in (away_lineup.players if away_lineup else [])
]
# Safeguard: Auto-start game if both lineups ready but game still pending
game_status = state.status
if home_count >= 9 and away_count >= 9 and game_status == "pending":
try:
logger.info(f"Auto-starting game {game_id} (in-memory) - both lineups ready but game was pending")
await game_engine.start_game(game_uuid)
game_status = "active"
except Exception as start_error:
logger.warning(f"Failed to auto-start game {game_id}: {start_error}")
return LineupStatusResponse(
game_id=game_id,
home_team_id=state.home_team_id,
@ -866,7 +876,7 @@ async def get_lineup_status(game_id: str):
away_lineup_submitted=away_count >= 9,
home_lineup_count=home_count,
away_lineup_count=away_count,
game_status=state.status,
game_status=game_status,
home_lineup=home_lineup_data,
away_lineup=away_lineup_data,
)
@ -905,6 +915,20 @@ async def get_lineup_status(game_id: str):
if lineup.player_id is not None
]
# Safeguard: Auto-start game if both lineups are ready but game is still pending
# This handles edge cases where lineups were submitted before the auto-start fix
game_status = game.status
if home_count >= 9 and away_count >= 9 and game_status == "pending":
try:
logger.info(f"Auto-starting game {game_id} - both lineups ready but game was pending")
# First recover game into state manager (required before start_game)
await state_manager.recover_game(game_uuid)
await game_engine.start_game(game_uuid)
game_status = "active"
except Exception as start_error:
logger.warning(f"Failed to auto-start game {game_id}: {start_error}")
# Continue with pending status - user can retry
return LineupStatusResponse(
game_id=game_id,
home_team_id=game.home_team_id,
@ -913,7 +937,7 @@ async def get_lineup_status(game_id: str):
away_lineup_submitted=away_count >= 9,
home_lineup_count=home_count,
away_lineup_count=away_count,
game_status=game.status,
game_status=game_status,
home_lineup=home_lineup_data,
away_lineup=away_lineup_data,
)

View File

@ -253,6 +253,14 @@ class GameEngine:
# Update state
state_manager.update_state(game_id, state)
# Broadcast game_state_update so all connected clients get the new active state
if self._connection_manager:
await self._connection_manager.broadcast_to_game(
str(game_id),
"game_state_update",
state.model_dump(mode='json')
)
# Emit decision_required event for real-time frontend notification
await self._emit_decision_required(game_id, state, "awaiting_defensive", timeout_seconds=self.DECISION_TIMEOUT)

View File

@ -322,7 +322,8 @@ class StateManager:
)
# Determine fielding team based on current half
current_half = game.get("current_half", "top")
# Use `or` to handle explicit None values from database (not just missing keys)
current_half = game.get("current_half") or "top"
home_team_id = game["home_team_id"]
away_team_id = game["away_team_id"]
@ -381,7 +382,7 @@ class StateManager:
home_team_is_ai=game.get("home_team_is_ai", False),
away_team_is_ai=game.get("away_team_is_ai", False),
status=game["status"],
inning=game.get("current_inning", 1),
inning=game.get("current_inning") or 1,
half=current_half,
home_score=game.get("home_score", 0),
away_score=game.get("away_score", 0),