Skip to content

docs: cs-bug scope audit (preflop-only vs broader) + perf claim re-validation#161

Merged
amaster97 merged 1 commit into
mainfrom
audit-cs-bug-scope-2026-05-28
May 28, 2026
Merged

docs: cs-bug scope audit (preflop-only vs broader) + perf claim re-validation#161
amaster97 merged 1 commit into
mainfrom
audit-cs-bug-scope-2026-05-28

Conversation

@amaster97

Copy link
Copy Markdown
Owner

Summary

Test plan

  • Read-only audit — no engine code modified
  • All citations are file:line (crates/cfr_core/src/preflop_rvr.rs:229-251, crates/cfr_core/src/hunl.rs:318, 382-414, crates/cfr_core/benches/preflop_rvr_profile.rs:42-66, etc.)
  • Cross-checked each State::initial dispatch against its leaf-utility formula
  • Verified Python guard at poker_solver/hunl.py:146-148 is the load-bearing safeguard

Doc-only PR. Auto-merge OK.

🤖 Generated with Claude Code

…lidation

Audit of `cs = contributions - initial_contributions` bug pattern (PR #159
context) across all subgame solver entry points.

Findings:
- Bug scope: PARTIAL. Same `State::initial` pattern in TWO solvers
  (`preflop_rvr` AND `preflop`), but Python `HUNLConfig.__post_init__`
  blocks the trigger combination (`Preflop` + `initial_contributions !=
  [0,0]`). Reachable only via Rust-direct callers bypassing Python.
- Postflop solvers (`solve_hunl_postflop`, `solve_range_vs_range_*`,
  exploit BR-walk) are NOT affected — they use the correct postflop
  branch at `crates/cfr_core/src/hunl.rs:318`.
- All shipped perf claims (#114, #139, #150, #157) STAND. Every bench
  either uses `initial_contributions=[0,0]` (degenerate-subtraction-safe)
  or the unaffected postflop solver.
- Recommendation: fix `PreflopRvrState::initial` (preflop_rvr.rs:229-251)
  + `HUNLState::initial_preflop` (hunl.rs:382-414) to honor
  `config.initial_contributions`, and add a Rust-side
  `HUNLConfig::validate()` mirroring Python's `__post_init__`.

No engine code touched — surface-only audit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@amaster97 amaster97 merged commit 63264dd into main May 28, 2026
5 of 8 checks passed
amaster97 added a commit that referenced this pull request May 28, 2026
…ns (#67) (#165)

Root cause documented in PR #159 and audited in PR #161: both
`PreflopRvrState::initial` and `HUNLState::initial_preflop`
unconditionally set `contributions=[SB+ante, BB+ante]` regardless of
`config.initial_contributions`. Downstream leaves compute
`cs = contributions - initial_contributions`, so a caller passing
`initial_contributions=[SB,BB]` with `initial_pot=0` (intending to
declare posted blinds) produced `cs=[0,0]` and `pot_total=0` at every
leaf — collapsing the preflop Nash to fold-everywhere.

Fix:
- `State::initial` honors `initial_contributions`:
  `contribution[i] = max(blind_amount[i], initial_contributions[i])`.
  Engine-posts-blinds (`[0,0]+pot=0`) and caller-declared-blinds
  (`[SB+ante,BB+ante]+pot=SB+BB+2*ante`) configs both produce
  Nash-equivalent strategies (per-leaf payoffs differ only by a
  constant per-player shift).
- Add Rust-side `HUNLConfig::validate()` mirroring
  `poker_solver/hunl.py:124-176` (defense-in-depth per
  `feedback_silent_skip_hazard` — PyO3 callers bypass Python's
  `__post_init__` guard).
- Wire `validate()` into `solve_hunl_preflop_rvr` so malformed
  configs (e.g., `[50,100]+pot=0`) fail loudly instead of returning
  a degenerate fold-everywhere equilibrium.

Tests:
- `crates/cfr_core/tests/preflop_initial_contributions.rs` (11
  Rust tests): AA-does-not-fold regression, identical-strategies
  diff-test between both well-formed configs, eight
  `HUNLConfig::validate()` accept/reject cases, and an entry-point
  rejection test for the bug-triggering config.
- `tests/test_preflop_rvr_diff.py::test_engine_agnostic_to_blind_declaration`:
  Python-side parametrized variant of the diff-test (max_drift <
  1e-4 between the two config shapes through the PyO3 boundary).

Existing tests verified clean:
- AA-vs-KK closed-form smoke (`preflop_rvr_smoke.rs`) still PASSes.
- All 4 `test_preflop_rvr_diff.py` Python diff tests PASS.
- `cargo test -p cfr_core --lib`: 75 unit tests PASS.
- `cargo clippy --lib --tests`: clean.
- `ruff check`: clean.

Postflop branch (`hunl.rs:318`) unchanged — it was already correct.
Leaf-payoff math unchanged — only `State::initial` and `validate()`.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant