Session Summary — 2026-06-27: Pot Odds Fix, Curve Sweeps, Call Mismatch, Gen 5 Design

# Session Summary — 2026-06-27: Pot Odds Fix, Curve Sweeps, Call Mismatch Fix, Gen 5 Design

## 1. Stack Loss Root Cause: Pot Odds Blind Spot in `should_raise`

### Bug
`should_raise()` in `rollout_postflop_gen3.rs` did NOT use pot odds, raise sizing, or SPR. The bot would happily shove all-in with medium hands facing bets, regardless of SPR or pot odds.

### Fix
Added three safety mechanisms to `should_raise()`:
1. **Large bet penalty**: When facing a bet >60% pot, threshold is increased proportionally
2. **SPR penalty**: When SPR < 3 and committing >50% of stack, threshold raised by 0.05
3. **Hard equity gate**: If hs < 0.60 and the raise would commit >100% of pot, threshold ≥ 0.65

### Regression Tests
- `hand_replay/tests/hand_tests.rs`: 2 new tests (18 total, all pass)
- **66 underpair shove bug**: Bot no longer shoves AllIn with pocket 66 on A-high river
- **77 underpair overplay**: Bot doesn't raise flop with underpair 77 on J-high board
- Test data files: `hand_replay/tests/data/66_underpair_shove_bug.txt`, `77_underpair_overplay.txt`

## 2. Curve Tuning Sweeps (3 Rounds Complete)

### Round 1 — Discovery Sweep (22 params)
Baseline: flock=928.72, chump=1326.82, combined=2255.55

Top winners (both opponents improve, marked ***):
| Param | Value | Combined Δ |
|---|---|---|
| bet_turn_base | 0.55 | +458.94 *** |
| raise_base | 0.80 | +70.68 *** |
| raise_w_protection | 0.35 | +60.64 *** |
| scurve_floor | 0.05 | +58.84 *** |
| bet_flop_base | 0.30 | +54.26 *** |
| bluff_hs_max | 0.05 | +52.97 *** |
| call_w_pot_odds | 0.78 | +46.23 *** |

### Round 2 — Narrowing & Curve Refinement
Baseline (post-R1): flock=1061.19, chump=1563.21

Top winners:
| Param | Value | Combined Δ |
|---|---|---|
| narrow_center_offset | 0.2 | +46.18 *** |
| range_looseness | 0.05 | +20.72 *** |
| scurve_floor | 0.03 | +16.25 *** |
| raise_w_protection | 0.40 | +6.37 *** |

### Round 3 — Final Tuning
Baseline (post-R2): flock=1125.79, chump=1584.53

Top winners:
| Param | Value | Combined Δ |
|---|---|---|
| range_tightness_scale | 0.8 | +30.50 *** |
| strong_hands_fraction | 0.30 | +18.10 *** |
| semi_bluff_max_bet_ratio | 0.30 | +15.80 *** |
| call_w_draw | 0.75 | +15.61 *** |

### Applied to `cash_nl_g4_live.toml`
All *** winners applied. Current config values:
```
bet_turn_base=0.58, bet_flop_base=0.30, raise_base=0.80
raise_w_protection=0.35, scurve_floor=0.05, scurve_steepness_mult=0.3
bluff_hs_max=0.05, semi_bluff_max_bet_ratio=0.30
range_tightness_scale=0.8, strong_hands_fraction=0.30
narrow_center_offset=0.2, range_looseness=0.05
call_w_draw=0.75, call_ppot_base=0.25
```

## 3. Call Mismatch Bug Fixed

### Root Cause
`reconcile_missing_blinds()` only ran at decision time (waiting_for_action). But call/raise events from other players arrive earlier. When the SB's blind wasn't delivered as an explicit event, `wagered_this_round=0`, so the server thought the SB owed the full BB instead of BB-SB.

62 call mismatches in the log, all caused by this timing issue.

### Fix
Call `reconcile_missing_blinds()` at the start of `apply_action()` for all **non-blind** actions (Call, Bet, Raise, Fold, Check). Skip for blind actions themselves to avoid synthesizing the other blind prematurely.

### Tests
- 21 live_server tests pass (was 20, added `reconcile_blinds_before_call_avoids_mismatch`)
- The "Raise did not exceed current_bet" warnings are **partial all-ins** — working correctly, no fix needed

## 4. Gen 5 Architecture Note Updated

Comprehensive update to Joplin note `bac569872f9149acbfa4832eb553312c` answering all design questions:

- **Range prediction NN vs current approach**: Start with current (feed model as features), add range NN in Phase 2 if needed
- **Record all hands**: YES — essential for training data
- **Preflop + postflop**: Same unified network handles both
- **RL in live play**: Not sufficient alone — need offline training first
- **Training curriculum**: Gen 1→2→3→4 with statistical advancement criteria
- **Table hopper**: Phase 2 feature, needs performance tracking
- **DOM snapshots**: Add to userscript for debugging + training data

## 5. Replay Engine

`hand_replay` crate with 18 tests:
- Parses PokerStars-format hand histories
- Replays against any registered bot type (including config-loaded bots)
- `scripts/convert_hand.py` converts live server logs to replay format
- Regression tests prevent known bugs from recurring

## 6. Files Changed This Session

### Modified
- `holdem_bots/src/cash/rollout_postflop_gen3.rs` — Pot odds fix in `should_raise()`
- `live_server/src/table_state.rs` — Blind reconciliation before non-blind actions
- `hand_replay/tests/hand_tests.rs` — 2 new regression tests
- `configs/bots/cash_nl_g4_live.toml` — All sweep winners applied
- `configs/bots/cash_nl_normal.toml` — Sweep winners applied
- `configs/bots/cash_nl_loose.toml` — Sweep winners applied

### New
- `hand_replay/tests/data/66_underpair_shove_bug.txt` — Regression test data
- `hand_replay/tests/data/77_underpair_overplay.txt` — Regression test data
- `scripts/overnight_validation.sh` — Validation sweep script

## 7. Overnight Work

- Validation sweep running: 30 sims × 20k hands (g4_live vs chump/flock, g2 vs chump)
- Gen 5 Joplin note fully updated with design decisions
- Next session: wire Gen5 recorder into live server, bridge PlayerModel→OpponentFeatures, start imitation learning data collection

id: 986f40b025fb454ab643b22d60cc5369
parent_id: 2c8da247905946c3aa19eb4936e16323
created_time: 2026-06-26T20:03:43.046Z
updated_time: 2026-06-28T05:18:11.595Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1782504223046
user_created_time: 2026-06-26T20:03:43.046Z
user_updated_time: 2026-06-27T18:56:13.400Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 1782623891595
type_: 1