Hand Audit #7 — Session 2 logging fixes + 34 decisions (2026-06-17 06:17–06:45 UTC)

# Hand Audit #7 — Session 2 logging fixes + 34 decisions (2026-06-17 06:17–06:45 UTC)

**Hero:** Bolsa, stack $255K → $237.5K (net **−$17.5K**)
**Blinds:** $500/$1000 NL
**Hands:** 24 | **Decisions:** 34 (22 Fold, 10 Check, 2 Raise)
**Bot:** `cash_nl` (g1 preflop + g2 postflop V3)
**Verdict:** Strategy sound on folds/checks; 2 raise spots worth review (deferred to strategy session)

---

## Logging bugs fixed this session ✅

Three bookkeeping bugs found via the audit loop and fixed in `live_server/src/hand_history.rs` + `table_state.rs`:

| Bug | Cause | Fix |
|-----|-------|-----|
| Duplicate hand blocks in `hand_history.log` | `flush()` re-appended the whole hand on every `Win` event; split/side pots (2+ winners) duplicated the block | `flush()` guarded by `flushed_to_history` flag; runs once per hand at next `start_hand` (+ GameOver/TableRemove fallback) |
| `$0` / wrong "Total pot" in summaries | Summary used server-tracked `self.pot`, which undercounts when blind events are missing | "Total pot" = sum of authoritative win amounts from events |
| Multi-winner double SUMMARY | Each `Win` emitted its own full summary block | Winners accumulate in `record_win()`, render as a single summary |
| Event-log boundary race | `GameStart` logged to previous hand's events file (logger swaps mid-handler) | Re-logged to the new hand's logger after `start_hand` |
| **sb_seat/bb_seat leak across hands** | `start_hand` reset the pot but not the blind-seat flags → reconciliation guard (`is_none()`) wrongly skipped synthesis | Reset `sb_seat`/`bb_seat = None` in `start_hand` |

Verified live: hands appear exactly once; split pots show one correct summary.

## Blind reconciliation hardened + VERIFIED ✅

Replaced the buggy `infer_bb_seat()` + inline BB-only synthesis with:
- `reconcile_missing_blinds()` — synthesizes **both** missing SB and BB (idempotent, guarded by `sb_seat`/`bb_seat` flags)
- `compute_blind_seats()` — correct HU (button=SB) and multi-way seat inference from clockwise dealt-in roster
- `start_hand` resets blind-seat flags so reconciliation fires on every hand that needs it

**Verification (2026-06-17): 51 hands / 137 decisions → ZERO low preflop pots.** 32 blind-synthesis events, all producing correct pots (e.g. SB +$500 → BB +$1000 → pot $1500). One `amount=0` synthesis was benign & correct (BB seat already all-in for the full amount). Script: `scripts/verify_blinds.sh`. **Open data issue D1 (HU blind detection) is now fully RESOLVED server-side.**

---

## Notable hands

### Hand #14 (f2dad644) — Q8o BTN, flop raise then turn fold — LOST ~$28K ⚠️
- **Q8o** BTN, limped pot, hero checks BB option
- Flop [Tc Qd 6d]: checks, then **raises to $30000** facing a $10000 bet — top pair, weak kicker (8), **8 active**, two diamonds
- Got 2 callers. Turn [Ad]: hero checks, faces $50500 bet, **folds**
- Net loss ~$28K. Raising top-pair-weak-kicker into 8 players is high-variance; turn Ace killed the hand
- *Strategy review deferred*

### Hand #15 (2da7fe5c) — AKs BTN raise, checked down — WON $22K ✓
- **AKs** (AdKd) BTN, raises to $5500 preflop ✓
- Flop [4s Kh 5s] / Turn [5h] / River [6s]: **checks all three** with top pair K + A kicker
- Won $22K at showdown (K-kicker held up)
- *Top pair checked down 3 streets = the open passive-value-bet issue (#1) — deferred*

### Hand #5 (a59f892) — A3o BTN, free check-down, river fold ✓
- Limped pot, A3o checks option through flop/turn/river, folds to river bet. Clean.

### Clean folds (16 hands)
Trash (74o, 43s, K4s, T6o, 62o, etc.) from all positions — all correct.

### One safety-guard conversion
`torn-holdem-1781677939082` (Qs6s): bot returned Check while facing a bet → converted to Fold. The guard fired correctly (GameInfo staleness); no illegal check sent.

---

## Open issues status (carried from Audit #2 / #6)

| # | Issue | Status |
|---|-------|--------|
| S1 | AQ/top-pair passive value betting (multi-way power factor) | **OPEN** (deferred — strategy session) |
| S2 | Draw-driven raise with no made hand (needs `draw_raise_min_strength`) | **OPEN** (deferred — strategy session) |
| D1 | HU blind detection (userscript never sends BB) | **RESOLVED** ✅ — verified 51 hands, zero low pots |
| D2 | Corrupt blinds after page reload (message replay) | **OPEN** — userscript-side |
| D3 | Executor: first raise didn't fire (66 hand, session 1) | **OPEN** — userscript-side |
| D4 | `cur_bet=1001 to_call=1` off-by-one | **OPEN** — userscript parsing |
| L1 | Duplicate hand blocks / $0 pots / double summaries | **FIXED** this session |
| L2 | Blind reconciliation amount=0 / SB missing / seat leak | **FIXED** this session |

## Files changed
- `live_server/src/hand_history.rs` — `record_win`/`full_lines`/guarded `flush`, 2 tests
- `live_server/src/table_state.rs` — `reconcile_missing_blinds`/`compute_blind_seats`/`active_dealt_seats_clockwise`/`synthesize_blind`; `start_hand` blind-flag reset; Win/start_hand/GameOver/TableRemove flush logic; event-boundary fix; 2 tests
- `scripts/audit_live.sh` — recurring 10-min audit (health, WARN scan, hand-history delta, decision grep)
- `scripts/verify_blinds.sh` — blind/pot correctness check

## Tooling now running
- **live_server** (port 8088, `RUST_LOG=debug`) → `/tmp/live_server.log`
- **Audit watcher** every 10 min → `/tmp/live_audit.log`
- Per-hand logs: `/tmp/super_marvin_hands/{hands,decisions,events}/`

id: 5e4427e43c7e4208b155e5bc1faf797e
parent_id: 22cdbfc0c00e404c83553543471f45ab
created_time: 2026-06-17T08:38:15.658Z
updated_time: 2026-06-17T18:51:48.292Z
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: 1781685495658
user_created_time: 2026-06-17T08:38:15.658Z
user_updated_time: 2026-06-17T16:50:48.166Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 1781722308292
type_: 1