Critical Fix: Personality VPIP Calibration + equity_v4 Collection

## Root Cause Found: Why NN Can't Predict Station Ranges

### Problem
The NN (range_v14_b4) was trained on data where **75% of samples had VPIP > 0.50** and **median VPIP was 0.876**. Real Torn players have VPIP 0.15-0.35. The NN literally never learned what a normal opponent looks like.

### Root Cause
Two compounding bugs:

1. **Personality configs had absurdly low equity thresholds**: Fish had `epf_equity_open_late = 0.18`. With the preflop_score formula (equity + weighted potentials), even 72o scores ~0.25 vs a 30% range. Threshold 0.18 lets EVERY hand pass → VPIP ~90%+.

2. **Table VPIP feedback loop**: The `effective_vpip()` function returns the observed table VPIP after 5 hands (`min_observed_hands = 5`). At a table full of loose bots, the table VPIP is high, making everyone play even looser. At a table of tight bots, everyone collapses to the same tight VPIP. ALL personalities converge to the same VPIP regardless of their config.

### Fix Applied (2026-08-01)
1. **Raised equity thresholds** for all personality configs:
   - Fish: open_early 0.25→0.42, open_late 0.18→0.34
   - Station: 0.30→0.38, 0.22→0.30
   - LAG: 0.38→0.40, 0.28→0.32
   - Maniac: 0.32→0.38, 0.24→0.30

2. **Set `epf_min_observed_hands = 999999`** for ALL personality configs (fish/station/lag/maniac/tag/nit/reg). This makes `effective_vpip()` always use `default_vpip` instead of table VPIP, breaking the feedback loop. Each personality maintains its intended VPIP:
   - Nit: ~0.10, TAG: ~0.20, Reg: ~0.25, Station: ~0.30, LAG: ~0.28, Fish: ~0.33, Maniac: ~0.35

### Verification (2000-hand 6-max sim)
- **Before**: median VPIP 0.876, 75% > 0.50
- **After**: median VPIP 0.248, range 0.08-0.40, 0% > 0.50

### Impact
This means ALL previous range models (v10-v14) were trained on garbage VPIP data. The NN couldn't distinguish opponent types because 75% of training samples had the same inflated VPID (~0.90). The station exploit (-200 BB/100 vs calling stations) is likely caused by this — the NN expects everyone to play 90% of hands and can't narrow ranges when facing a real station with VPIP 0.35.

### Next Steps
1. **equity_v4 collection running** (8 tables × 62500 hands, ~12 hours)
2. Retrain as range_v15 when collection completes
3. Re-run sanity checks with corrected NN
4. This should fix the station exploit without any formula changes

### Configs Modified
- `configs/bots/g48_fish.toml` — thresholds raised, min_observed_hands=999999
- `configs/bots/g48_station.toml` — same
- `configs/bots/g48_lag.toml` — same
- `configs/bots/g48_maniac.toml` — same
- `configs/bots/g48_tag.toml` — min_observed_hands=999999 only
- `configs/bots/g48_nit.toml` — min_observed_hands=999999 only
- `configs/bots/g48_reg.toml` — min_observed_hands=999999 only

### Note on Production Bot
The production g50 config still uses `epf_min_observed_hands = 5` (table-adaptive). This is correct for production — the bot SHOULD adapt to real human table VPIP. The `999999` setting is only for collection opponents to generate diverse training data.


id: b1967faf0653447b929f2f7d79c36565
parent_id: 5a06903f
created_time: 2026-08-01T20:51:01.888Z
updated_time: 2026-08-01T20:51:01.888Z
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: 1785617461889
user_created_time: 2026-08-01T20:51:01.888Z
user_updated_time: 2026-08-01T20:51:01.888Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
is_locked: 0
extracted_resource_ids: 
type_: 1