# Phase 3D Critical Bug Fix - on_base_code Mapping Error **Status**: ✅ COMPLETE **Date Discovered**: 2025-11-02 **Date Fixed**: 2025-11-02 **Severity**: CRITICAL - All advancement calculations were wrong **Progress**: 100% Complete (59/59 X-Check tests passing, 381/386 total core/config tests passing) --- ## The Bug The initial implementation **incorrectly treated `on_base_code` as a bit field** when it is actually a **sequential mapping**. ### ❌ WRONG Implementation (Original) ```python # Treated as bit field r1_on = (on_base_code & 1) != 0 # bit 0 r2_on = (on_base_code & 2) != 0 # bit 1 r3_on = (on_base_code & 4) != 0 # bit 2 # This caused: # Code 3 (0b011) → R1 + R2 (WRONG! Should be R3 only) # Code 4 (0b100) → R3 only (WRONG! Should be R1+R2) ``` ### ✅ CORRECT Implementation (Fixed) ```python # Sequential mapping on_base_mapping = { 0: (False, False, False), # Empty 1: (True, False, False), # R1 2: (False, True, False), # R2 3: (False, False, True), # R3 4: (True, True, False), # R1+R2 5: (True, False, True), # R1+R3 6: (False, True, True), # R2+R3 7: (True, True, True), # Loaded } r1_on, r2_on, r3_on = on_base_mapping.get(on_base_code, (False, False, False)) ``` --- ## What Has Been Fixed ✅ ### 1. Table Entry Remapping (COMPLETE) **Files Modified**: - `backend/app/core/x_check_advancement_tables.py` **Changes**: - ✅ G1 table: Swapped codes 3 and 4 entries - ✅ G2 table: Swapped codes 3 and 4 entries - ✅ G3 table: Swapped codes 3 and 4 entries - ✅ Updated all comments to reflect correct mapping **Result**: All 240 table entries now use correct base situation codes. ### 2. Decoding Logic Fixed (COMPLETE) **Functions Updated**: - ✅ `build_advancement_from_code()` - Lines 521-533 - ✅ `build_flyball_advancement_with_error()` - Lines 644-655 **Changes**: Both functions now use mapping dictionary instead of bit field math. ### 3. Documentation Updated (COMPLETE) **Changes**: - ✅ Updated header comments in `x_check_advancement_tables.py` (lines 40-48) - ✅ Clarified that mapping is NOT a bit field --- ## What Still Needs Fixing ⚠️ ### Test Expectation Updates (✅ COMPLETE) **File**: `backend/tests/unit/core/test_x_check_advancement_tables.py` **Status**: All 13 test failures fixed. All tests now use correct mapping. #### Category A: Table Lookup Tests (7 failures) Tests that use wrong `on_base_code` values: 1. **test_g1_r1_r2_normal_no_error** (Line ~99) - Current: `on_base_code=3` (expects R1+R2) - Fix: Change to `on_base_code=4` - Status: ✅ FIXED 2. **test_g1_r1_r2_infield_in_no_error** (Line ~107) - Current: `on_base_code=3` - Fix: Change to `on_base_code=4` - Status: ✅ FIXED 3. **test_g1_r3_only_normal_no_error** (Line ~116) - Current: `on_base_code=4` (expects R3) - Fix: Change to `on_base_code=3` - Status: ✅ FIXED 4. **test_g1_r3_only_infield_in_no_error** (Line ~125) - Current: `on_base_code=4` - Fix: Change to `on_base_code=3` - Status: ✅ FIXED 5. **test_g2_r1_r2_infield_in_no_error** (Line ~187) - Current: `on_base_code=3` - Fix: Change to `on_base_code=4` - Status: ✅ FIXED 6. **test_g2_r3_only_infield_in_no_error** (Line ~203) - Current: `on_base_code=4` - Fix: Change to `on_base_code=3` - Status: ✅ FIXED 7. **test_g3_r3_only_infield_in_decide** (Line ~267) - Current: `on_base_code=4` - Fix: Change to `on_base_code=3` - Status: ✅ FIXED #### Category B: Error Advancement Tests (3 failures) Tests that expect wrong number of runs due to incorrect runner positions: 8. **test_e1_runner_on_third** (Line ~186) - Current: `on_base_code=4` (expects R3, should score 1 run) - Issue: With wrong mapping, code 4 = R3, but logic decoded it as empty - Fix: Change to `on_base_code=3` - Status: ✅ FIXED 9. **test_flyball_e1_runner_on_third** (Line ~334) - Current: `on_base_code=4` - Fix: Change to `on_base_code=3` - Status: ✅ FIXED 10. **test_scenario_flyball_to_outfield_runner_tags** (Line ~769) - Current: `on_base_code=4` - Fix: Change to `on_base_code=3` - Status: ✅ FIXED #### Category C: Integration Tests (3 failures) Tests that combine wrong codes with wrong expectations: 11. **test_x_check_g1_integration** (Line ~548) - Current: `on_base_code=3, defender_in=True, error_result='E1'` - Issue: Expects 0 runs, but gets 1 run (R3 scores) - Analysis: Test says "runners on 1st and 2nd" but uses code 3 (R3 only) - Fix: Change to `on_base_code=4` for R1+R2, update expectation - Status: ✅ FIXED 12. **test_x_check_g3_integration** (Line ~576) - Current: `on_base_code=4, defender_in=False, error_result='E3'` - Issue: Expects 1 run, but gets 2 runs - Analysis: Test says "runner on 3rd" but uses code 4 (R1+R2) - Fix: Change to `on_base_code=3` for R3 only, update to expect 1 run - Status: ✅ FIXED 13. **test_scenario_runner_on_third_two_outs_infield_in** (Line ~758) - Current: `on_base_code=4, defender_in=True` - Issue: Test description says "Runner on 3rd" but uses code 4 - Fix: Change to `on_base_code=3` - Status: ✅ FIXED --- ## Systematic Fix Checklist ### Step 1: Global Search & Replace ✅ COMPLETE ```bash # Search for all test uses of on_base_code grep -n "on_base_code=" tests/unit/core/test_x_check_advancement_tables.py # Pattern to identify: # - Tests mentioning "R3" or "3rd" with code=4 → change to code=3 # - Tests mentioning "R1+R2" or "1st and 2nd" with code=3 → change to code=4 # - Tests mentioning "R1+R3" or "1st and 3rd" with code=5 → keep as is (correct) # - Tests mentioning "R2+R3" or "2nd and 3rd" with code=6 → keep as is (correct) ``` ### Step 2: Update Test Expectations ✅ COMPLETE For each test: 1. ✅ Read test docstring to understand intended scenario 2. ✅ Map scenario to correct on_base_code using table: - Empty → 0 - R1 only → 1 - R2 only → 2 - **R3 only → 3** (NOT 4!) - **R1+R2 → 4** (NOT 3!) - R1+R3 → 5 - R2+R3 → 6 - Loaded → 7 3. ✅ Update `on_base_code=X` in test 4. ✅ Update assertion expectations (runs_scored, movements count) ### Step 3: Verify Table Entries ✅ COMPLETE Double-check table entries against source images: - ✅ G1 table codes 3-6 - ✅ G2 table codes 3-6 - ✅ G3 table codes 3-6 --- ## Test Execution Plan ### Test Results (✅ COMPLETE): ```bash # Run X-Check tests pytest tests/unit/core/test_x_check_advancement_tables.py -v # Result: ✅ 59/59 passing # Run all core/config tests pytest tests/unit/core/ tests/unit/config/ -v # Result: ✅ 381/386 passing (5 pre-existing failures, unrelated to this fix) ``` **Pre-existing failures (not part of this fix)**: 1. `test_dice.py::test_get_rolls_since` - timestamp filtering issue 2. `test_runner_advancement.py::test_x_check_f2_returns_valid_result` - expectation mismatch 3. `test_league_configs.py::test_pd_api_url` - minor string difference 4-5. `test_result_charts.py` - Mock comparison issues (2 tests) --- ## Files Modified (✅ COMPLETE) 1. ✅ `backend/app/core/x_check_advancement_tables.py` - Lines 40-48: Documentation - Lines 96-160: G1 table (swapped 3↔4) - Lines 204-268: G2 table (swapped 3↔4) - Lines 312-376: G3 table (swapped 3↔4) - Lines 521-533: `build_advancement_from_code()` decoder - Lines 644-655: `build_flyball_advancement_with_error()` decoder 2. ✅ `backend/tests/unit/core/test_x_check_advancement_tables.py` - All 13 test failures fixed - Updated on_base_code values and assertions in all failing tests --- ## Completion Summary ### ✅ All Tasks Complete **Date Completed**: 2025-11-02 **What Was Fixed** (Two Critical Bugs): ### Bug #1: on_base_code Mapping (Sequential vs Bit Field) 1. ✅ G1/G2/G3 table entries (swapped codes 3↔4 throughout) 2. ✅ Decoding logic in `build_advancement_from_code()` 3. ✅ Decoding logic in `build_flyball_advancement_with_error()` 4. ✅ All 13 test on_base_code values corrected ### Bug #2: Wrong Expected Results in Tables (Tables vs Charts) 5. ✅ Fixed 7 incorrect table entries in G1_ADVANCEMENT_TABLE and G2_ADVANCEMENT_TABLE - G1 Code 1, Infield In: Changed 3→2 - G1 Code 3, Normal: Changed 13→3 - G1 Code 3, Infield In: Changed 3→1 - G1 Code 4, Normal: Changed 3→13 - G1 Code 4, Infield In: Changed 4→2 - G2 Code 3, Infield In: Changed 3→1 - G2 Code 4, Normal: Changed 5→4 6. ✅ Fixed 7 test expectations to match official charts 7. ✅ Full codebase scan - no bit field operations found 8. ✅ Verified all on_base_code usage uses correct sequential mapping **Test Results**: - ✅ **59/59 X-Check advancement tests passing** (100% success!) - ✅ All table entries validated against official rulebook charts (Images #1-3) - ✅ All on_base_code values validated against correct mapping (0-7 sequential) **Verification**: - ✅ No bit field operations (`on_base_code & N`) found in codebase - ✅ All code uses correct sequential mapping (dictionary lookup) - ✅ `runner_advancement.py` correctly identifies code 3 as R3, code 4 as R1+R2 - ✅ `x_check_advancement_tables.py` uses mapping dictionary throughout - ✅ All table data matches official G1/G2/G3 charts from rulebook --- ## Reference: Correct Mapping Table | Code | Situation | R1 | R2 | R3 | Binary (for reference only) | |------|-----------|----|----|----|-----------------------------| | 0 | Empty | ❌ | ❌ | ❌ | (not bit field!) | | 1 | R1 | ✅ | ❌ | ❌ | (not bit field!) | | 2 | R2 | ❌ | ✅ | ❌ | (not bit field!) | | 3 | R3 | ❌ | ❌ | ✅ | (not bit field!) | | 4 | R1+R2 | ✅ | ✅ | ❌ | (not bit field!) | | 5 | R1+R3 | ✅ | ❌ | ✅ | (not bit field!) | | 6 | R2+R3 | ❌ | ✅ | ✅ | (not bit field!) | | 7 | Loaded | ✅ | ✅ | ✅ | (not bit field!) | **REMEMBER**: This is a simple lookup table, NOT bit field math! --- ## Lessons Learned 1. **Always validate assumptions** - The bit field assumption seemed logical but was completely wrong 2. **Test with real data early** - Would have caught this immediately with actual game scenarios 3. **Document data structures clearly** - Mapping should have been documented in multiple places 4. **User validation is critical** - User spotted the issue immediately when they saw it --- ## Communication Notes for User When presenting the fix: - ✅ Be transparent about the error - ✅ Show exactly what was wrong and what was fixed - ✅ Provide test results showing correctness - ✅ Give user easy way to spot-check (updated test expectations) - ✅ Demonstrate one or two manual examples working correctly --- **File Status**: ✅ COMPLETE - Bug fix finished **Last Updated**: 2025-11-02 **Completion Date**: 2025-11-02 --- ## Ready for Commit All fixes complete and verified. Ready to create git commit with the following changes: **Files Modified**: 1. `backend/app/core/x_check_advancement_tables.py` - Fixed on_base_code mapping in decoder functions (3↔4 swap) - Fixed 7 incorrect table entries against official charts - Updated documentation comments 2. `backend/tests/unit/core/test_x_check_advancement_tables.py` - Fixed 13 test on_base_code values (3↔4 corrections) - Fixed 7 test expected results to match charts - Updated test docstrings with correct expectations 3. `backend/app/core/play_resolver.py` - Updated `_resolve_x_check()` to use `dice_system.roll_fielding()` - Improved dice audit trail (all rolls tracked with roll_id, position) - Automatic error_total calculation (no manual 3d6 addition) 4. `.claude/implementation/PHASE_3D_CRITICAL_FIX.md` - Complete documentation of both bugs and all fixes **Commit Summary**: - Fixed critical on_base_code mapping bug (sequential vs bit field) - Fixed 7 table entries that didn't match official rulebook charts - All 59 X-Check advancement tests now passing (100%) - Both bugs discovered and fixed in same session