feat: Uncapped hit decision tree, x-check workflow, baserunner UI #8

Merged
cal merged 11 commits from feature/uncapped-hit-decision-tree into main 2026-02-12 15:37:34 +00:00
Owner

Summary- Uncapped hit decision tree (Issue #6): Full interactive multi-step workflow for SINGLE_UNCAPPED/DOUBLE_UNCAPPED outcomes with 5 decision phases (lead runner advance, defensive throw, trail runner advance, throw target, safe/out speed check)- Interactive x-check workflow: XCheckWizard component with WebSocket integration, position-based result selection, and store wiring- Baserunner horizontal layout: New RunnersOnBase/RunnerCard components with expanding cards, runner/catcher matchup display, and mobile-responsive stacking- On-base code encoding fix: Corrected sequential chart encoding for current_on_base_code (was bit-field, now matches runner advancement charts)- Double play recovery bug fix: Fixed state recovery for double play scenarios- Frontend test fixes: Added Pinia setup to GameplayPanel tests, updated DefensiveSetup tests## Test Results- Backend: 2481/2481 unit tests passing (80 new for uncapped hits, 1184 invariant tests, 220 truth table tests)- Frontend: 496/496 tests passing (28 fixed in GameplayPanel.spec.ts)## Test plan- [ ] Run backend unit tests: cd backend && uv run pytest tests/unit- [ ] Run frontend tests: cd frontend-sba && npm test- [ ] Verify uncapped hit workflow triggers on SINGLE_UNCAPPED/DOUBLE_UNCAPPED outcomes- [ ] Verify x-check wizard displays and submits results correctly- [ ] Verify baserunner panel shows runners with expand/collapse behavior- [ ] Test mobile layout stacking for pitcher/batter and runners🤖 Generated with Claude Code

## Summary- **Uncapped hit decision tree** (Issue #6): Full interactive multi-step workflow for SINGLE_UNCAPPED/DOUBLE_UNCAPPED outcomes with 5 decision phases (lead runner advance, defensive throw, trail runner advance, throw target, safe/out speed check)- **Interactive x-check workflow**: XCheckWizard component with WebSocket integration, position-based result selection, and store wiring- **Baserunner horizontal layout**: New RunnersOnBase/RunnerCard components with expanding cards, runner/catcher matchup display, and mobile-responsive stacking- **On-base code encoding fix**: Corrected sequential chart encoding for `current_on_base_code` (was bit-field, now matches runner advancement charts)- **Double play recovery bug fix**: Fixed state recovery for double play scenarios- **Frontend test fixes**: Added Pinia setup to GameplayPanel tests, updated DefensiveSetup tests## Test Results- Backend: 2481/2481 unit tests passing (80 new for uncapped hits, 1184 invariant tests, 220 truth table tests)- Frontend: 496/496 tests passing (28 fixed in GameplayPanel.spec.ts)## Test plan- [ ] Run backend unit tests: `cd backend && uv run pytest tests/unit`- [ ] Run frontend tests: `cd frontend-sba && npm test`- [ ] Verify uncapped hit workflow triggers on SINGLE_UNCAPPED/DOUBLE_UNCAPPED outcomes- [ ] Verify x-check wizard displays and submits results correctly- [ ] Verify baserunner panel shows runners with expand/collapse behavior- [ ] Test mobile layout stacking for pitcher/batter and runners🤖 Generated with [Claude Code](https://claude.com/claude-code)
cal added 11 commits 2026-02-12 15:35:45 +00:00
Backend changes:
- Add PendingXCheck model for interactive x-check state
- Extend decision_phase/pending_decision validators with 4 new phases
- Add initiate_x_check() to roll dice and present chart to player
- Add submit_x_check_result() to process player selection
- Add resolve_x_check_from_selection() to resolve from player input
- Add WebSocket handlers for x-check workflow
- Modify resolve_manual_play() to route X_CHECK to interactive flow
- All 986 unit tests passing

Frontend changes:
- Extend DecisionPhase type with x-check/DECIDE phases
- Add XCheckData, DecideAdvanceData, DecideThrowData, DecideSpeedCheckData interfaces
- Add PendingXCheck to GameState
- Add 4 new client→server WebSocket events

Next: Implement XCheckWizard component and GameplayPanel integration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Test coverage:
- Creation with minimal fields (required only)
- Creation with optional fields (SPD, result selection, DECIDE)
- Field validation (d20, d6, chart_row, error_result, etc.)
- Range constraints (d20: 1-20, d6: 1-6, bases: proper values)
- Mutability during workflow (can update selections)

Results:
- 19 new tests, all passing
- Total: 1005 unit tests passing (was 986)
- PendingXCheck model fully validated

Next: Create XCheckWizard frontend component

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
New files:
- constants/xCheckResults.ts - Labels, helpers for all result codes
- components/Gameplay/XCheckWizard.vue - Interactive x-check UI

XCheckWizard features:
 Displays d20 and 3d6 dice results prominently
 Shows 5-column chart row (Range 1-5) as selectable buttons
 Hash result sub-choices (G2#/G3# → pick G2 or SI2)
 SPD result sub-choice (click to reveal d20, pick safe/out)
 Error selection (NO/E1/E2/E3/RP) based on 3d6
 Submit validation (both result + error required)
 Read-only mode for transparency (opponent sees same UI)
 Mobile-responsive layout (stacks on small screens)
 Tailwind styling with clear visual hierarchy

Helper functions:
- getResultLabel() - Display names for all codes
- getErrorLabel() - Display names for error types
- isHashResult() - Detect G2#/G3#
- isSpdResult() - Detect SPD
- getHashConversions() - Get conversion options

Next: Integrate XCheckWizard into GameplayPanel

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Step 7 of x-check interactive workflow implementation:

Frontend Integration:
- GameplayPanel.vue: Add x_check_result_pending workflow state, show XCheckWizard when decision_phase is awaiting_x_check_result, handle interactive vs read-only mode based on active_team_id
- store/game.ts: Add xCheckData and decideData state, add needsXCheckResult/needsDecide* getters, add set/clear actions for x-check and decide data
- useWebSocket.ts: Handle decision_required events with x_check_result/decide_advance/decide_throw/decide_speed_check types, route to appropriate store actions, clear x-check/decide data on play_resolved
- useGameActions.ts: Add submitXCheckResult(), submitDecideAdvance(), submitDecideThrow(), submitDecideResult() action wrappers
- types: Export XCheckData, DecideAdvanceData, DecideThrowData, DecideSpeedCheckData, PendingXCheck, and new WebSocket request types

Type fixes:
- XCheckData: Allow readonly arrays for d6_individual and chart_row (store returns readonly refs)
- GameplayPanel: Add userTeamId prop for determining interactive mode

Tests: 460 passing, 28 failing (GameplayPanel.spec.ts needs Pinia setup - pre-existing issue)

Next: Step 8 - End-to-end testing of basic x-check flow (no DECIDE)
- Replace .border-l-4.border-gray-600 checks with .catcher-pill
- Update isExpanded prop references to isSelected
- Adjust expanded view tests for new side-by-side layout
  - matchup-card-blue = runner full card
  - matchup-card = catcher full card
- Fix deselection test to check isSelected prop (Transition keeps DOM during animation)

All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
- Swap base order to 3B, 2B, 1B (left to right, closer to baseball diamond)
- Auto-select lead runner on mount (priority: 3B > 2B > 1B)
- Make catcher pill clickable to show catcher card only
- Add 'catcher' as a selection option alongside runner bases
- Update expanded view to handle catcher-only display (centered, single card)
- Add toggleCatcher() function
- Update tests for new base order and auto-selection behavior

All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
- When catcher pill is clicked, display lead runner (3B > 2B > 1B priority) + catcher side-by-side
- Maintains consistent layout whether runner or catcher is selected
- Add leadRunnerBase computed to find highest priority runner
- Add displayedRunnerBase computed to show selected OR lead runner
- Update template to use displayedRunnerPlayer for consistent rendering

All 15 RunnersOnBase tests passing
Runner highlights and cards:
- Pills: red-500 ring, red-50 background when selected
- Full cards: red gradient (red-900 to red-950), red-600 border
- Pulse glow: red animation (rgba(239, 68, 68))
- Hardcoded red color (#ef4444) for runner pill borders

Catcher highlights and cards:
- Pill: blue-500 ring, blue-50 background when selected
- Full card: blue gradient (blue-900 to blue-950), blue-600 border
- Pulse glow: blue animation (rgba(59, 130, 246))

Updated tests to expect new colors

All 15 RunnersOnBase tests passing
All 16 RunnerCard tests passing
Fix critical encoding mismatch where calculate_on_base_code() returned
bit-field encoding (3=R1+R2, 4=R3) but runner_advancement.py charts
expected sequential encoding (3=R3, 4=R1+R2). Values 3 and 4 were
swapped, causing wrong groundball results for R1+R2 and R3-only scenarios.

Add comprehensive test coverage:
- 1184 invariant tests (structural correctness across all outcomes × base codes)
- 49 hit truth table tests (SINGLE_1/2, DOUBLE_2/3, TRIPLE, HOMERUN)
- 33 walk truth table tests (WALK, HBP with stat flag verification)
- 42 simple out truth table tests (STRIKEOUT, LINEOUT, POPOUT, WP, PB)
- 88 groundball truth table tests (GB_A/B/C × infield back/in/corners_in × locations)

Total: 2401 unit tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add full multi-step decision workflow for SINGLE_UNCAPPED and DOUBLE_UNCAPPED
outcomes, replacing the previous stub that fell through to basic single/double
advancement. The decision tree follows the same interactive pattern as X-Check
resolution with 5 phases: lead runner advance, defensive throw, trail runner
advance, throw target selection, and safe/out speed check.

- game_models.py: PendingUncappedHit model, 5 new decision phases
- game_engine.py: initiate_uncapped_hit(), 5 submit methods, 3 result builders
- handlers.py: 5 new WebSocket event handlers
- ai_opponent.py: 5 AI decision stubs (conservative defaults)
- play_resolver.py: Updated TODO comments for fallback paths
- 80 new backend tests (2481 total): workflow (49), handlers (23), truth tables (8)
- Fix GameplayPanel.spec.ts: add missing Pinia setup, fix component references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cal reviewed 2026-02-12 15:36:39 +00:00
cal left a comment
Author
Owner

LGTM

LGTM
cal merged commit ffcbe248bd into main 2026-02-12 15:37:34 +00:00
cal deleted branch feature/uncapped-hit-decision-tree 2026-02-12 15:37:34 +00:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: cal/strat-gameplay-webapp#8
No description provided.