CLAUDE: Add WebSocket protocol specification and implementation guides
Comprehensive documentation for real-time gameplay workflow: **New Documentation**: 1. WEBSOCKET_PROTOCOL_SPEC.md (49KB) - Complete catalog of all 15 backend WebSocket event handlers - Complete catalog of all frontend event listeners - Game workflow sequences (connection → game start → play resolution) - Critical issues identified and resolution status - Event payload specifications with examples - Timing and performance expectations 2. DECISION_REQUIRED_IMPLEMENTATION.md (11KB) - Issue #1 detailed analysis and resolution - Backend implementation of decision_required event - Frontend integration approach - Before/After workflow comparison - Test validation results 3. GAMEPLAY_SESSION_HANDOFF.md (10KB) - Session work summary and accomplishments - Live testing results and observations - Known issues and next steps - Quick start guide for next session - Technical architecture notes **Why**: - Provides single source of truth for WebSocket protocol - Documents complete event flow for frontend/backend alignment - Captures implementation decisions and rationale - Enables faster onboarding for new developers - Creates reference for debugging WebSocket issues **Impact**: - Reduces protocol confusion between frontend and backend - Accelerates future WebSocket feature development - Provides clear integration patterns for new events 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1373286391
commit
f3eb5e8200
335
.claude/DECISION_REQUIRED_IMPLEMENTATION.md
Normal file
335
.claude/DECISION_REQUIRED_IMPLEMENTATION.md
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# Decision Required Event Implementation
|
||||||
|
|
||||||
|
**Date**: 2025-01-21
|
||||||
|
**Status**: IMPLEMENTED - Pending Testing
|
||||||
|
**Priority**: HIGH (from WebSocket Protocol Spec Issue #1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Implemented real-time `decision_required` event emission from backend to frontend. Previously, the frontend had to poll `gameState.decision_phase` to know when decisions were needed. Now the backend proactively notifies the frontend via WebSocket events.
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
From WEBSOCKET_PROTOCOL_SPEC.md Issue #1:
|
||||||
|
- Frontend expects `decision_required` event when decision phases change
|
||||||
|
- Backend never emitted this event
|
||||||
|
- Workaround: Frontend polls `gameState.decision_phase`
|
||||||
|
- Impact: Inconsistent with real-time design philosophy
|
||||||
|
|
||||||
|
## Solution Implemented
|
||||||
|
|
||||||
|
### 1. GameEngine Infrastructure (game_engine.py)
|
||||||
|
|
||||||
|
**Added Connection Manager Integration** (lines 42-58):
|
||||||
|
```python
|
||||||
|
def __init__(self):
|
||||||
|
# ... existing code ...
|
||||||
|
self._connection_manager = None # Set by main.py
|
||||||
|
|
||||||
|
def set_connection_manager(self, connection_manager):
|
||||||
|
"""Set WebSocket connection manager for real-time events"""
|
||||||
|
self._connection_manager = connection_manager
|
||||||
|
logger.info("WebSocket connection manager configured for game engine")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Created Event Emission Helper** (lines 60-100):
|
||||||
|
```python
|
||||||
|
async def _emit_decision_required(
|
||||||
|
self, game_id: UUID, state: GameState, phase: str, timeout_seconds: int = 300
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Emit decision_required event to notify frontend.
|
||||||
|
|
||||||
|
Auto-determines which team (home/away) needs to decide based on:
|
||||||
|
- awaiting_defensive: Fielding team (home if top, away if bottom)
|
||||||
|
- awaiting_offensive: Batting team (away if top, home if bottom)
|
||||||
|
"""
|
||||||
|
if not self._connection_manager:
|
||||||
|
logger.warning("No connection manager - cannot emit decision_required")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Team determination logic
|
||||||
|
if phase == "awaiting_defensive":
|
||||||
|
role = "home" if state.half == "top" else "away"
|
||||||
|
elif phase == "awaiting_offensive":
|
||||||
|
role = "away" if state.half == "top" else "home"
|
||||||
|
|
||||||
|
await self._connection_manager.broadcast_to_game(
|
||||||
|
str(game_id),
|
||||||
|
"decision_required",
|
||||||
|
{
|
||||||
|
"phase": phase,
|
||||||
|
"role": role,
|
||||||
|
"timeout_seconds": timeout_seconds,
|
||||||
|
"message": f"{role.title()} team: {phase.replace('_', ' ').title()} decision required"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Event Emission Points
|
||||||
|
|
||||||
|
**Location 1: Game Start** (game_engine.py:253)
|
||||||
|
```python
|
||||||
|
async def start_game(self, game_id: UUID) -> GameState:
|
||||||
|
# ... existing setup code ...
|
||||||
|
state.decision_phase = "awaiting_defensive"
|
||||||
|
state_manager.update_state(game_id, state)
|
||||||
|
|
||||||
|
# NEW: Emit decision_required event
|
||||||
|
await self._emit_decision_required(
|
||||||
|
game_id, state, "awaiting_defensive",
|
||||||
|
timeout_seconds=self.DECISION_TIMEOUT
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Location 2: Defensive Decision Request** (game_engine.py:398)
|
||||||
|
```python
|
||||||
|
async def _request_defensive_decision(
|
||||||
|
self, state: GameState, timeout: int = DECISION_TIMEOUT
|
||||||
|
) -> DefensiveDecision:
|
||||||
|
# ... existing code ...
|
||||||
|
state.decision_phase = "awaiting_defensive"
|
||||||
|
state_manager.update_state(state.game_id, state)
|
||||||
|
|
||||||
|
# NEW: Emit decision_required event
|
||||||
|
await self._emit_decision_required(
|
||||||
|
state.game_id, state, "awaiting_defensive",
|
||||||
|
timeout_seconds=timeout
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Location 3: Offensive Decision Request** (game_engine.py:464)
|
||||||
|
```python
|
||||||
|
async def _request_offensive_decision(
|
||||||
|
self, state: GameState, timeout: int = DECISION_TIMEOUT
|
||||||
|
) -> OffensiveDecision:
|
||||||
|
# ... existing code ...
|
||||||
|
state.decision_phase = "awaiting_offensive"
|
||||||
|
state_manager.update_state(state.game_id, state)
|
||||||
|
|
||||||
|
# NEW: Emit decision_required event
|
||||||
|
await self._emit_decision_required(
|
||||||
|
state.game_id, state, "awaiting_offensive",
|
||||||
|
timeout_seconds=timeout
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Main.py Integration (main.py:86-88)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Initialize connection manager and register handlers
|
||||||
|
connection_manager = ConnectionManager(sio)
|
||||||
|
register_handlers(sio, connection_manager)
|
||||||
|
|
||||||
|
# NEW: Configure game engine with connection manager
|
||||||
|
from app.core.game_engine import game_engine
|
||||||
|
game_engine.set_connection_manager(connection_manager)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Event Payload Specification
|
||||||
|
|
||||||
|
### Event Name
|
||||||
|
`decision_required`
|
||||||
|
|
||||||
|
### Direction
|
||||||
|
Server → Client (Broadcast to game room)
|
||||||
|
|
||||||
|
### Payload Structure
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
phase: "awaiting_defensive" | "awaiting_offensive",
|
||||||
|
role: "home" | "away",
|
||||||
|
timeout_seconds: number,
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Payloads
|
||||||
|
|
||||||
|
**Defensive (Top of inning)**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phase": "awaiting_defensive",
|
||||||
|
"role": "home",
|
||||||
|
"timeout_seconds": 30,
|
||||||
|
"message": "Home team: Awaiting Defensive decision required"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Offensive (Bottom of inning)**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phase": "awaiting_offensive",
|
||||||
|
"role": "home",
|
||||||
|
"timeout_seconds": 30,
|
||||||
|
"message": "Home team: Awaiting Offensive decision required"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend Integration
|
||||||
|
|
||||||
|
### Existing Handler (useWebSocket.ts:289-292)
|
||||||
|
|
||||||
|
Frontend already has the handler implemented:
|
||||||
|
```typescript
|
||||||
|
socketInstance.on('decision_required', (prompt) => {
|
||||||
|
console.log('[WebSocket] Decision required:', prompt.phase)
|
||||||
|
gameStore.setDecisionPrompt(prompt)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expected Behavior
|
||||||
|
1. Backend emits `decision_required` when phase transitions
|
||||||
|
2. Frontend receives event
|
||||||
|
3. Frontend updates `currentDecisionPrompt` in game store
|
||||||
|
4. UI reactively shows appropriate decision panel (DefensiveSetup or OffensiveApproach)
|
||||||
|
|
||||||
|
### Frontend Type Definition (types/game.ts:268-276)
|
||||||
|
|
||||||
|
Already defined:
|
||||||
|
```typescript
|
||||||
|
export interface DecisionPrompt {
|
||||||
|
phase: DecisionPhase
|
||||||
|
role: 'home' | 'away'
|
||||||
|
timeout_seconds: number
|
||||||
|
options?: string[]
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Status
|
||||||
|
|
||||||
|
### ✅ Completed
|
||||||
|
- [x] Code implementation in game_engine.py
|
||||||
|
- [x] Connection manager integration in main.py
|
||||||
|
- [x] Backend server restarts cleanly with changes
|
||||||
|
- [x] Type definitions already exist in frontend
|
||||||
|
- [x] Frontend handler already exists
|
||||||
|
|
||||||
|
### ⏳ Pending
|
||||||
|
- [ ] End-to-end test: Create game → Submit lineups → Verify event received
|
||||||
|
- [ ] Verify event arrives when game starts (awaiting_defensive)
|
||||||
|
- [ ] Verify event arrives after defensive decision submitted (awaiting_offensive)
|
||||||
|
- [ ] Verify frontend DecisionPanel shows proactively (without polling)
|
||||||
|
- [ ] Update WEBSOCKET_PROTOCOL_SPEC.md to mark Issue #1 as RESOLVED
|
||||||
|
|
||||||
|
## Testing Instructions
|
||||||
|
|
||||||
|
### Manual Test Plan
|
||||||
|
|
||||||
|
1. **Start Both Servers**:
|
||||||
|
```bash
|
||||||
|
# Backend
|
||||||
|
cd backend && uv run python -m app.main
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
cd frontend-sba && bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create New Game**:
|
||||||
|
- Navigate to http://localhost:3000
|
||||||
|
- Create game with two teams
|
||||||
|
- Submit lineups for both teams
|
||||||
|
|
||||||
|
3. **Verify Event Emission**:
|
||||||
|
- Backend logs should show: `Emitted decision_required for game {id}: phase=awaiting_defensive, role=home`
|
||||||
|
- Frontend logs should show: `[WebSocket] Decision required: awaiting_defensive`
|
||||||
|
- Frontend should show DefensiveSetup panel immediately (no polling delay)
|
||||||
|
|
||||||
|
4. **Submit Defensive Decision**:
|
||||||
|
- Set defensive positioning
|
||||||
|
- Submit decision
|
||||||
|
- Verify offensive_decision_required event arrives
|
||||||
|
- Verify OffensiveApproach panel shows
|
||||||
|
|
||||||
|
5. **Check Browser DevTools**:
|
||||||
|
- Network tab → WS → Messages
|
||||||
|
- Should see `decision_required` events in real-time
|
||||||
|
|
||||||
|
### Backend Logs to Watch
|
||||||
|
|
||||||
|
```
|
||||||
|
2025-01-21 XX:XX:XX - app.core.game_engine.GameEngine - INFO - WebSocket connection manager configured for game engine
|
||||||
|
2025-01-21 XX:XX:XX - app.core.game_engine.GameEngine - INFO - Emitted decision_required for game {uuid}: phase=awaiting_defensive, role=home
|
||||||
|
2025-01-21 XX:XX:XX - app.core.game_engine.GameEngine - INFO - Emitted decision_required for game {uuid}: phase=awaiting_offensive, role=away
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Logs to Watch
|
||||||
|
|
||||||
|
```
|
||||||
|
[WebSocket] Decision required: awaiting_defensive
|
||||||
|
[Game Store] Decision prompt set: { phase: 'awaiting_defensive', role: 'home', ... }
|
||||||
|
[DecisionPanel] Showing defensive setup panel
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Changes
|
||||||
|
|
||||||
|
### Companion Implementation: Decision Phase Naming (2025-01-21)
|
||||||
|
|
||||||
|
As part of this session, we also standardized decision phase naming:
|
||||||
|
- All code now uses `'awaiting_defensive'` and `'awaiting_offensive'`
|
||||||
|
- Removed dual-condition checks in frontend
|
||||||
|
- See: `types/game.ts:41`, `store/game.ts:109-125`
|
||||||
|
|
||||||
|
This ensures consistency between the `decision_required` event and `gameState.decision_phase` values.
|
||||||
|
|
||||||
|
## Known Issues / Limitations
|
||||||
|
|
||||||
|
1. **No Authorization Check**: Event is broadcast to entire game room, including spectators
|
||||||
|
- Future: Add team ownership validation
|
||||||
|
- Related: Protocol Spec Issue #9 (TODO comments)
|
||||||
|
|
||||||
|
2. **No Retry Logic**: If WebSocket is disconnected during emission, event is lost
|
||||||
|
- Mitigation: Frontend still polls `gameState.decision_phase` as fallback
|
||||||
|
- Future: Add event queuing for reconnection
|
||||||
|
|
||||||
|
3. **Timeout Not Enforced**: `timeout_seconds` is informational only
|
||||||
|
- Backend has timeout logic in `_request_defensive_decision` but uses asyncio.wait_for
|
||||||
|
- Frontend doesn't show countdown timer yet
|
||||||
|
|
||||||
|
4. **Decision Phase Field Sync** (RESOLVED 2025-11-21):
|
||||||
|
- Issue: `submit_defensive_decision` and `submit_offensive_decision` were setting `pending_decision` but NOT `decision_phase`
|
||||||
|
- Impact: Frontend received `decision_required` event but game state showed stale phase
|
||||||
|
- Fix: Added `state.decision_phase` updates in both methods:
|
||||||
|
- Line 291: `state.decision_phase = "awaiting_offensive"` after defensive
|
||||||
|
- Line 335: `state.decision_phase = "resolution"` after offensive
|
||||||
|
- Why needed: Frontend checks `gameState.decision_phase`, backend must keep it in sync
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Immediate**: Run manual test plan above
|
||||||
|
2. **If tests pass**: Update protocol spec to mark Issue #1 as RESOLVED
|
||||||
|
3. **Next priority**: Implement authorization checks (Protocol Spec Issue #9)
|
||||||
|
4. **Future enhancement**: Add countdown timer UI in DecisionPanel
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- `backend/app/core/game_engine.py` (+58 lines)
|
||||||
|
- Added `_connection_manager` attribute
|
||||||
|
- Added `set_connection_manager()` method
|
||||||
|
- Added `_emit_decision_required()` helper
|
||||||
|
- Added 3 emission calls at phase transitions
|
||||||
|
|
||||||
|
- `backend/app/main.py` (+3 lines)
|
||||||
|
- Import game_engine singleton
|
||||||
|
- Call `game_engine.set_connection_manager(connection_manager)`
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- No changes needed - handler already implemented
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If issues arise, revert these commits:
|
||||||
|
1. Remove emission calls from game_engine.py (lines 253, 398, 464)
|
||||||
|
2. Remove `_emit_decision_required()` method (lines 60-100)
|
||||||
|
3. Remove connection_manager integration from main.py (lines 86-88)
|
||||||
|
4. Frontend will fallback to polling `gameState.decision_phase`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document Status**: Ready for testing
|
||||||
|
**Last Updated**: 2025-01-21
|
||||||
|
**Author**: Claude (Session: decision_required implementation)
|
||||||
338
.claude/GAMEPLAY_SESSION_HANDOFF.md
Normal file
338
.claude/GAMEPLAY_SESSION_HANDOFF.md
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
# Gameplay Loop Implementation - Session Handoff
|
||||||
|
|
||||||
|
**Date**: 2025-01-21
|
||||||
|
**Session Focus**: Connecting frontend gameplay UI to backend WebSocket handlers
|
||||||
|
**Status**: 🟡 In Progress - Defensive decisions working, testing offensive workflow next
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Session Goals
|
||||||
|
|
||||||
|
Implement the core gameplay loop: dice rolling → outcome submission → play resolution
|
||||||
|
|
||||||
|
**Target Workflow**:
|
||||||
|
1. User submits defensive setup (infield/outfield positioning)
|
||||||
|
2. User submits offensive decision (batting action)
|
||||||
|
3. User rolls dice
|
||||||
|
4. User submits manual outcome (from physical card)
|
||||||
|
5. Backend resolves play and broadcasts result
|
||||||
|
6. Game state updates, play appears in feed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ What We Accomplished
|
||||||
|
|
||||||
|
### 1. **Connected Gameplay Loop Components**
|
||||||
|
- Fixed `submitManualOutcome()` signature in `useGameActions.ts`
|
||||||
|
- Added `play_resolved` WebSocket event handler
|
||||||
|
- Fixed game page to call outcome submission correctly
|
||||||
|
- Added missing TypeScript imports
|
||||||
|
|
||||||
|
### 2. **Fixed Decision Panel Visibility Issues**
|
||||||
|
|
||||||
|
**Root Cause**: Frontend was checking for `decision_phase: "defense"` but backend uses `"awaiting_defensive"`
|
||||||
|
|
||||||
|
**Files Modified**:
|
||||||
|
- `frontend-sba/store/game.ts` - Added checks for both naming conventions:
|
||||||
|
```typescript
|
||||||
|
needsDefensiveDecision: checks 'defense' OR 'awaiting_defensive'
|
||||||
|
needsOffensiveDecision: checks 'offensive_approach' OR 'awaiting_offensive'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Fixed Game Start Issues**
|
||||||
|
|
||||||
|
**Problem**: Games were stuck in `decision_phase: "idle"` after lineups submitted
|
||||||
|
|
||||||
|
**Root Causes Fixed**:
|
||||||
|
1. Missing `game_engine` import in `backend/app/api/routes/games.py`
|
||||||
|
2. `start_game()` wasn't setting `decision_phase` after preparing first play
|
||||||
|
|
||||||
|
**Files Modified**:
|
||||||
|
- `backend/app/api/routes/games.py`:
|
||||||
|
```python
|
||||||
|
from app.core.game_engine import game_engine # Added import
|
||||||
|
```
|
||||||
|
|
||||||
|
- `backend/app/core/game_engine.py`:
|
||||||
|
```python
|
||||||
|
# In start_game() after _prepare_next_play():
|
||||||
|
state.decision_phase = "awaiting_defensive" # Added this line
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. **Fixed State Updates After Decision Submission**
|
||||||
|
|
||||||
|
**Problem**: After submitting defensive decision, UI stayed on defensive panel instead of advancing to offensive
|
||||||
|
|
||||||
|
**Solution**: Frontend now requests updated game state after decision submissions
|
||||||
|
|
||||||
|
**File Modified**:
|
||||||
|
- `frontend-sba/composables/useWebSocket.ts`:
|
||||||
|
```typescript
|
||||||
|
socketInstance.on('defensive_decision_submitted', (data) => {
|
||||||
|
// Request updated game state to get new decision_phase
|
||||||
|
socketInstance.emit('request_game_state', { game_id: gameStore.gameId })
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. **Added Debug Logging**
|
||||||
|
- Added console logging in game page to debug panel visibility
|
||||||
|
- Logs show `decision_phase`, `needsDefensiveDecision`, `needsOffensiveDecision`, etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Files Modified (Need Committing)
|
||||||
|
|
||||||
|
### Backend Changes
|
||||||
|
```
|
||||||
|
backend/app/api/routes/games.py
|
||||||
|
backend/app/core/game_engine.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Changes
|
||||||
|
```
|
||||||
|
frontend-sba/store/game.ts
|
||||||
|
frontend-sba/composables/useGameActions.ts
|
||||||
|
frontend-sba/composables/useWebSocket.ts
|
||||||
|
frontend-sba/pages/games/[id].vue
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ What's Working Now
|
||||||
|
|
||||||
|
### ✓ Game Creation Flow
|
||||||
|
- Create game → submit lineups → game auto-starts
|
||||||
|
- Game enters `"awaiting_defensive"` phase immediately
|
||||||
|
- Frontend shows **Defensive Setup** panel
|
||||||
|
|
||||||
|
### ✓ Defensive Decision Submission
|
||||||
|
- User can select infield/outfield positioning
|
||||||
|
- Clicking "Submit" sends decision to backend
|
||||||
|
- Backend receives and processes decision
|
||||||
|
- Emits `defensive_decision_submitted` event
|
||||||
|
- Frontend requests updated game state
|
||||||
|
|
||||||
|
### ✓ WebSocket Communication
|
||||||
|
- Connection established successfully
|
||||||
|
- Events flowing backend → frontend
|
||||||
|
- `dice_rolled`, `outcome_accepted`, `play_resolved` handlers implemented
|
||||||
|
|
||||||
|
### ✓ Backend Game Engine
|
||||||
|
- All 15 WebSocket handlers implemented (Phase 3E-Final)
|
||||||
|
- Manual outcome workflow complete
|
||||||
|
- Play resolution working
|
||||||
|
- Database persistence functional
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Current State
|
||||||
|
|
||||||
|
### What User Just Did
|
||||||
|
1. ✅ Created new game (ID: `0411d0ce-f41f-46eb-b0ac-23bae818d6ad`)
|
||||||
|
2. ✅ Saw defensive setup panel appear
|
||||||
|
3. ✅ Submitted defensive decision
|
||||||
|
4. ⚠️ Still seeing "Your Defensive Turn" indicator
|
||||||
|
|
||||||
|
### What Should Happen Next
|
||||||
|
After latest fix (requesting game state after submission), user should:
|
||||||
|
1. Refresh game page
|
||||||
|
2. Submit defensive decision again
|
||||||
|
3. Should see **Offensive Approach** panel appear
|
||||||
|
4. Select batting action → Submit
|
||||||
|
5. Should see **Gameplay Panel** with **Roll Dice** button
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testing Checklist
|
||||||
|
|
||||||
|
### ✅ Tested & Working
|
||||||
|
- [x] Game creation
|
||||||
|
- [x] Lineup submission
|
||||||
|
- [x] Game auto-start
|
||||||
|
- [x] Defensive panel appears
|
||||||
|
- [x] Defensive decision sends to backend
|
||||||
|
|
||||||
|
### ⏳ Next to Test
|
||||||
|
- [ ] Defensive submission → Offensive panel transition
|
||||||
|
- [ ] Offensive decision submission
|
||||||
|
- [ ] Offensive → Resolution phase (dice rolling)
|
||||||
|
- [ ] Dice roll button click
|
||||||
|
- [ ] Dice results display
|
||||||
|
- [ ] Manual outcome entry
|
||||||
|
- [ ] Outcome submission
|
||||||
|
- [ ] Play resolution display
|
||||||
|
- [ ] Play-by-play feed update
|
||||||
|
- [ ] Game state updates (score, outs, runners)
|
||||||
|
- [ ] Second at-bat (loop continues)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Technical Details
|
||||||
|
|
||||||
|
### Backend Decision Phase Values
|
||||||
|
```python
|
||||||
|
"idle" # Initial state (before start)
|
||||||
|
"awaiting_defensive" # Waiting for defensive setup
|
||||||
|
"awaiting_offensive" # Waiting for offensive approach
|
||||||
|
"resolution" # Ready for dice rolling
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Event Flow
|
||||||
|
|
||||||
|
**Decision Submission**:
|
||||||
|
```
|
||||||
|
Frontend: submit_defensive_decision →
|
||||||
|
Backend: process → emit defensive_decision_submitted →
|
||||||
|
Frontend: request_game_state →
|
||||||
|
Backend: game_state (with decision_phase: "awaiting_offensive") →
|
||||||
|
Frontend: Show offensive panel
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dice Rolling & Outcome**:
|
||||||
|
```
|
||||||
|
Frontend: roll_dice →
|
||||||
|
Backend: roll dice → emit dice_rolled →
|
||||||
|
Frontend: Show dice results →
|
||||||
|
User: Read card, select outcome →
|
||||||
|
Frontend: submit_manual_outcome →
|
||||||
|
Backend: validate & resolve → emit play_resolved →
|
||||||
|
Frontend: Display result, update state
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Store Properties
|
||||||
|
```typescript
|
||||||
|
// Game Store (frontend-sba/store/game.ts)
|
||||||
|
needsDefensiveDecision // Shows defensive panel
|
||||||
|
needsOffensiveDecision // Shows offensive panel
|
||||||
|
canRollDice // Enables dice roll button
|
||||||
|
canSubmitOutcome // Enables outcome submission
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Known Issues & Workarounds
|
||||||
|
|
||||||
|
### Issue 1: Frontend Compilation Warnings
|
||||||
|
**Symptom**: "Element is missing end tag" errors in console
|
||||||
|
**Impact**: None - server still runs fine
|
||||||
|
**Status**: Cosmetic, doesn't affect functionality
|
||||||
|
|
||||||
|
### Issue 2: Old Games Stuck in "idle"
|
||||||
|
**Symptom**: Games created before our fixes still show "Waiting for strategic decisions"
|
||||||
|
**Workaround**: Create fresh games - old games won't auto-advance
|
||||||
|
**Fix**: Old games need manual state update or recreation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start for Next Session
|
||||||
|
|
||||||
|
### 1. Check Server Status
|
||||||
|
```bash
|
||||||
|
# Backend should be running on port 8000
|
||||||
|
# Frontend should be running on port 3001
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Test Defensive → Offensive Transition
|
||||||
|
1. Go to current game: `http://localhost:3001/games/0411d0ce-f41f-46eb-b0ac-23bae818d6ad`
|
||||||
|
2. Submit defensive decision
|
||||||
|
3. **Should** see offensive panel appear now (after latest fix)
|
||||||
|
|
||||||
|
### 3. If Offensive Panel Appears - Continue Testing!
|
||||||
|
- Submit offensive decision
|
||||||
|
- Click "Roll Dice"
|
||||||
|
- Check dice results display
|
||||||
|
- Submit outcome
|
||||||
|
- Verify play resolution
|
||||||
|
|
||||||
|
### 4. If Still Stuck - Create Fresh Game
|
||||||
|
```
|
||||||
|
http://localhost:3001/games/create
|
||||||
|
```
|
||||||
|
New games will have all fixes applied.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Pending Commits
|
||||||
|
|
||||||
|
### Gameplay Loop Connection (Already Committed)
|
||||||
|
```bash
|
||||||
|
git log --oneline -1
|
||||||
|
# 58b5deb CLAUDE: Connect gameplay loop - dice rolling and play resolution
|
||||||
|
```
|
||||||
|
|
||||||
|
### Decision Panel Fixes (Need Committing)
|
||||||
|
```bash
|
||||||
|
# Backend changes
|
||||||
|
backend/app/api/routes/games.py # Added game_engine import
|
||||||
|
backend/app/core/game_engine.py # Set decision_phase in start_game
|
||||||
|
|
||||||
|
# Frontend changes
|
||||||
|
frontend-sba/store/game.ts # Check both decision_phase naming conventions
|
||||||
|
frontend-sba/composables/useWebSocket.ts # Request state after decision submissions
|
||||||
|
frontend-sba/pages/games/[id].vue # Added debug logging
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Lessons Learned
|
||||||
|
|
||||||
|
### 1. Backend/Frontend Contract Mismatches
|
||||||
|
**Issue**: Backend used "awaiting_defensive" but frontend checked for "defense"
|
||||||
|
**Solution**: Frontend now checks both variants for robustness
|
||||||
|
|
||||||
|
### 2. State Synchronization
|
||||||
|
**Issue**: Backend updated state but didn't broadcast changes
|
||||||
|
**Solution**: Frontend explicitly requests updated state after key events
|
||||||
|
|
||||||
|
### 3. Game Initialization
|
||||||
|
**Issue**: `start_game()` prepared play but didn't set decision_phase
|
||||||
|
**Solution**: Explicitly set initial decision_phase after prepare
|
||||||
|
|
||||||
|
### 4. Import Issues Can Fail Silently
|
||||||
|
**Issue**: Missing `game_engine` import caused silent failure with try/catch
|
||||||
|
**Solution**: Always verify imports are present and check logs for errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 If You Get Stuck
|
||||||
|
|
||||||
|
### Check These First
|
||||||
|
1. **Backend logs**: `BashOutput` with filter for game ID or event names
|
||||||
|
2. **Browser console**: Look for `[Game Page] Panel visibility check:` log
|
||||||
|
3. **WebSocket events**: Check if events are being received
|
||||||
|
4. **Game state**: Look at `decision_phase` value in console log
|
||||||
|
|
||||||
|
### Common Fixes
|
||||||
|
- **Panels not showing**: Check `decision_phase` value, create fresh game
|
||||||
|
- **Events not working**: Check WebSocket connection status
|
||||||
|
- **State not updating**: Look for `game_state` or `game_state_update` events
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Success Criteria
|
||||||
|
|
||||||
|
**Session Complete When**:
|
||||||
|
- [x] Defensive panel appears ✅
|
||||||
|
- [ ] Offensive panel appears after defensive submission
|
||||||
|
- [ ] Dice can be rolled
|
||||||
|
- [ ] Outcome can be submitted
|
||||||
|
- [ ] Play resolves and appears in feed
|
||||||
|
- [ ] Second at-bat starts automatically
|
||||||
|
- [ ] All changes committed
|
||||||
|
|
||||||
|
**Current Progress**: ~60% complete
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 References
|
||||||
|
|
||||||
|
- **Backend Handlers**: `backend/app/websocket/handlers.py` (15 handlers, all implemented)
|
||||||
|
- **Game Engine**: `backend/app/core/game_engine.py` (Phase 3E-Final complete)
|
||||||
|
- **Frontend Components**: `frontend-sba/components/Gameplay/` (all built, partially connected)
|
||||||
|
- **Main Session Context**: `CLAUDE.md` in project root
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next Session Goal**: Complete offensive decision → dice rolling → outcome submission flow
|
||||||
|
|
||||||
|
Good luck! 🚀
|
||||||
1831
.claude/WEBSOCKET_PROTOCOL_SPEC.md
Normal file
1831
.claude/WEBSOCKET_PROTOCOL_SPEC.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user