pokertools-arena 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +8 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/bin/pokertools-arena.mjs +197 -0
- package/dist/.nojekyll +0 -0
- package/dist/app.js +118427 -0
- package/dist/arena-env.js +2 -0
- package/dist/favicon.svg +4 -0
- package/dist/index.html +382 -0
- package/dist/og-image.png +0 -0
- package/dist/og-image.svg +190 -0
- package/dist/pokertools-arena.html +121106 -0
- package/dist/styles.css +2289 -0
- package/docs/architecture/CONTEXT.md +202 -0
- package/docs/diagnostics/SUMMARY.md +102 -0
- package/docs/diagnostics/summary.json +337 -0
- package/docs/legal/THIRD_PARTY_NOTICES.md +15 -0
- package/docs/releases/RELEASE_NOTES.md +171 -0
- package/docs/verification/0.3.x.md +250 -0
- package/package.json +77 -0
- package/src/app.js +2458 -0
- package/src/assets/favicon.svg +4 -0
- package/src/assets/og-image.png +0 -0
- package/src/assets/og-image.svg +190 -0
- package/src/benchmark/scenarios.js +108 -0
- package/src/env/arena-env.js +2 -0
- package/src/index.html +382 -0
- package/src/lib/decision-core.js +1288 -0
- package/src/shims/crypto.cjs +19 -0
- package/src/styles.css +2289 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Release notes
|
|
2
|
+
|
|
3
|
+
## 0.4.0 — Methodology, reproducibility and repository organization
|
|
4
|
+
|
|
5
|
+
This release does not redesign the 0.3.0 decision foundations. It makes the
|
|
6
|
+
benchmark more rigorous, separates the questions being answered, and reorganizes
|
|
7
|
+
the repository so there is one source of truth for code, tools, tests and
|
|
8
|
+
documentation.
|
|
9
|
+
|
|
10
|
+
### Repository
|
|
11
|
+
|
|
12
|
+
- Browser application source now lives under `src/` (`src/app.js`,
|
|
13
|
+
`src/index.html`, `src/styles.css`, `src/lib/decision-core.js`,
|
|
14
|
+
`src/benchmark/scenarios.js`, `src/env/arena-env.js`, `src/shims/crypto.cjs`,
|
|
15
|
+
`src/assets/`). There are no duplicate root copies.
|
|
16
|
+
- Developer-only implementation lives under `tools/diagnostics/` and
|
|
17
|
+
`tools/release/`; tests are grouped into `tests/unit`, `tests/integration`,
|
|
18
|
+
`tests/real` and `tests/analysis`; documentation lives under `docs/`.
|
|
19
|
+
- `build.mjs`, `bin/pokertools-arena.mjs`, every import, npm script, GitHub
|
|
20
|
+
workflow and documentation link were updated for the new layout.
|
|
21
|
+
|
|
22
|
+
### Methodology
|
|
23
|
+
|
|
24
|
+
- **Paired fixed-state corpus** (`tools/diagnostics/corpus.js`): immutable,
|
|
25
|
+
production-shaped decision states covering strict legality/dominance, value
|
|
26
|
+
betting, bluffing, preflop, hand recognition and representation sensitivity.
|
|
27
|
+
Gold answers exist only where strict dominance or an explicit synthetic
|
|
28
|
+
opponent policy makes them deterministic.
|
|
29
|
+
- **Paired flat-vs-hierarchical test** (`tools/diagnostics/paired.js`): the same
|
|
30
|
+
model, state, context, mode and representation under both architectures, with
|
|
31
|
+
deterministic interleaving and a recorded `experimentSeed`. This is the
|
|
32
|
+
primary architecture comparison; real tournaments are end-to-end validation.
|
|
33
|
+
- **Family vs sizing correctness** are reported separately, with sizing accuracy
|
|
34
|
+
conditional on a correct family, plus final-action accuracy.
|
|
35
|
+
- **`fragmentation_flip_rate`**: the share of paired runs whose broad family
|
|
36
|
+
changes when only the number of same-family sizing choices changes, across
|
|
37
|
+
menus A/B/C/D and multiple states.
|
|
38
|
+
- **Exact entropy validation and domain labelling**: `familyEntropyBits`,
|
|
39
|
+
`sizingEntropyBits` and `flatActionEntropyBits`; a generic “entropy” is never
|
|
40
|
+
reported.
|
|
41
|
+
- **Standardized counters** (`tools/diagnostics/counters.js`): `pokerDecisions`,
|
|
42
|
+
`familyModelCalls`, `sizingModelCalls`, `spectatorModelCalls`,
|
|
43
|
+
`totalModelCalls`, `httpRequests`, `httpRetries`, `rateLimitResponses`,
|
|
44
|
+
`decisionErrors`, `fallbackActions`, `protocolFallbacks`, with asserted
|
|
45
|
+
relationships.
|
|
46
|
+
- **Wilson 95% confidence intervals and sample sizes** for behavioral
|
|
47
|
+
proportions, so 2/2 and 100/100 are not presented with equal strength.
|
|
48
|
+
- **One generated summary** (`tools/diagnostics/report.js`) drives every
|
|
49
|
+
human-readable report; a release test fails if committed documentation drifts
|
|
50
|
+
from the machine-readable totals.
|
|
51
|
+
- **Spectator explanations default off** in rigorous runs; when enabled they are
|
|
52
|
+
isolated and counted separately.
|
|
53
|
+
- **Size-bucket boundary audit** for tiny/huge stacks, min-bet/min-raise edges,
|
|
54
|
+
all-in below min raise, all-in near LARGE, pot smaller than the big blind,
|
|
55
|
+
heads-up, multiway and post-elimination states.
|
|
56
|
+
|
|
57
|
+
### Testing and CI
|
|
58
|
+
|
|
59
|
+
- `npm run test:unit`, `test:integration`, `test:real`, `test:tournament-ab`,
|
|
60
|
+
`report:diagnostics` and `release:archive` replace the ad-hoc script names.
|
|
61
|
+
- CI runs install → build → release checks → unit/fairness → integration and
|
|
62
|
+
never requires real API credentials. Real diagnostics run from a manual,
|
|
63
|
+
secret-gated workflow.
|
|
64
|
+
- The release archive is built from the new layout, excludes secrets/logs/dist,
|
|
65
|
+
and is verified by an archive-content regression test.
|
|
66
|
+
|
|
67
|
+
### Compatibility
|
|
68
|
+
|
|
69
|
+
- The browser build, GitHub Pages deployment, `npx pokertools-arena` launcher and
|
|
70
|
+
automatic `.env` bootstrap are preserved.
|
|
71
|
+
- `@pokertools/engine@1.0.20` and `@pokertools/evaluator@1.0.20` are unchanged.
|
|
72
|
+
- The `.env` file is never moved, printed or archived.
|
|
73
|
+
|
|
74
|
+
## 0.3.0 — Fair hierarchical decisions, Strategy/Raw modes, deterministic hand evaluation
|
|
75
|
+
|
|
76
|
+
This release is a fairness and correctness release. It changes how the benchmark
|
|
77
|
+
turns a poker state into a decision so that CHECK, CALL, FOLD, BET and RAISE are
|
|
78
|
+
represented fairly for **every** model, regardless of how many bet sizes an
|
|
79
|
+
arena happens to generate.
|
|
80
|
+
|
|
81
|
+
### The problem
|
|
82
|
+
|
|
83
|
+
A single passive strategy was represented by one class (e.g. `CHECK`) while a
|
|
84
|
+
single aggressive strategy could be represented by many classes (`BET 1 BB`,
|
|
85
|
+
`BET 2 BB`, `BET 3 BB`, …, `BET ALL-IN`). Under a flat argmax this fragmenting
|
|
86
|
+
of the aggressive probability mass made passive actions look dominant even when
|
|
87
|
+
the model's total aggressive probability was larger.
|
|
88
|
+
|
|
89
|
+
### What changed
|
|
90
|
+
|
|
91
|
+
- **Hierarchical decision architecture (`hierarchical-v1`).** Every model is now
|
|
92
|
+
asked two questions:
|
|
93
|
+
1. **Action family** — `CHECK`/`BET` when nothing is owed, or
|
|
94
|
+
`FOLD`/`CALL`/`RAISE` when facing a bet. Only legal families are exposed.
|
|
95
|
+
2. **Size** — only when the family is `BET` or `RAISE`, from a deterministic,
|
|
96
|
+
engine-validated size set (`SMALL`, `MEDIUM`, `LARGE`, `ALL_IN`).
|
|
97
|
+
A single passive strategy is one family; a single aggressive strategy is one
|
|
98
|
+
family. The model cannot change the family during the sizing step.
|
|
99
|
+
- **One canonical decision contract.** `src/lib/decision-core.js` is the single
|
|
100
|
+
source of truth for decision state, action families, sizing choices, Jev
|
|
101
|
+
questions, chat schemas and the final engine action. Production, diagnostics
|
|
102
|
+
and tests all use the same functions.
|
|
103
|
+
- **Deterministic sizing.** `legalAggressiveSizes(engine, seat, family)` clamps
|
|
104
|
+
to the available stack, respects the minimum bet/raise, deduplicates and drops
|
|
105
|
+
near-duplicate amounts, always includes all-in when materially distinct, and
|
|
106
|
+
works for short stacks (a single `ALL_IN` or `SMALL`/`ALL_IN` is valid).
|
|
107
|
+
- **No prose in the primary decision.** Chat models no longer return
|
|
108
|
+
`publicReason` inside the action call. The primary contract contains only
|
|
109
|
+
typed decision fields. Spectator explanations are a separate, isolated,
|
|
110
|
+
off-the-clock concern (disabled by default; Jev never gets fabricated prose).
|
|
111
|
+
- **Strategy / Raw benchmark modes.** A tournament-wide setting:
|
|
112
|
+
* **Strategy** — every model receives a deterministic `heroHand`
|
|
113
|
+
(`{ category, description }`) generated by `@pokertools/evaluator`.
|
|
114
|
+
* **Raw cognition** — no `heroHand`; models infer strength from raw cards.
|
|
115
|
+
The mode applies to all seats and is recorded in tournament state, logs,
|
|
116
|
+
reports and replays. Opponent hand evaluations are never exposed.
|
|
117
|
+
- **Representation modes.** `canonical_json` (default), `compact_json` and
|
|
118
|
+
`markdown`, applied to every seat.
|
|
119
|
+
- **Shared action clock.** The family and size calls share one abort signal and
|
|
120
|
+
one action clock. A model does not receive a fresh timer for the sizing stage.
|
|
121
|
+
- **Decision event schema.** `decisionMeta` now carries
|
|
122
|
+
`decisionArchitecture`, `family` and `sizing` (choice, probabilities,
|
|
123
|
+
confidence, latency) plus `finalAction`. Events record `primaryDecisionLatencyMs`
|
|
124
|
+
(family latency + sizing latency, excluding spectator explanations).
|
|
125
|
+
- **Legacy aggregation.** `aggregateActionProbabilitiesByFamily` folds old flat
|
|
126
|
+
probability vectors into family mass for replay and log analysis, labelled
|
|
127
|
+
“Aggregated from legacy flat action probabilities”. Historical selected actions
|
|
128
|
+
are never changed.
|
|
129
|
+
- **Jev telemetry.** Internal identifiers (`A0`, `A1`, …) are translated through
|
|
130
|
+
the actual choice map. Spectators see family probabilities, sizing
|
|
131
|
+
probabilities, confidence bands, aggression tendency and bluff-opportunity
|
|
132
|
+
telemetry rather than invented text rationales.
|
|
133
|
+
- **Diagnostics.** New `action_fragmentation`, `flat vs hierarchical`, `probe`
|
|
134
|
+
matrix and family-entropy/telemetry sections in
|
|
135
|
+
`tests/real/real-decision-diagnostics.mjs`; new offline fairness suite
|
|
136
|
+
`tests/unit/hierarchical-decision-tests.mjs`; real tournament A/B harness
|
|
137
|
+
`tests/real/real-tournament-ab.mjs`; concise summary builder
|
|
138
|
+
`tests/analysis/diagnostics-summary.mjs`.
|
|
139
|
+
|
|
140
|
+
### Compatibility
|
|
141
|
+
|
|
142
|
+
- Flat legacy mode is retained **only** as a diagnostics baseline
|
|
143
|
+
(`decisionArchitecture: "flat"`). Hierarchical is the production default.
|
|
144
|
+
- `@pokertools/engine@1.0.20` and `@pokertools/evaluator@1.0.20` are unchanged.
|
|
145
|
+
- Existing `.env` bootstrap, GitHub Pages and `npx pokertools-arena` workflows
|
|
146
|
+
are unchanged.
|
|
147
|
+
|
|
148
|
+
### Measured effect (real API, 2026-09-19)
|
|
149
|
+
|
|
150
|
+
369 decisions across Gemma, Qwen and Jev, 0 request errors, 415 HTTP requests.
|
|
151
|
+
|
|
152
|
+
Action-fragmentation experiment on identical river states:
|
|
153
|
+
|
|
154
|
+
| Menu | Gemma | Qwen | Jev |
|
|
155
|
+
| --- | --- | --- | --- |
|
|
156
|
+
| A · CHECK / BET ALL-IN | 0% | 89% | 100% |
|
|
157
|
+
| B · CHECK / BET SMALL / ALL-IN | 0% | 56% | 56% |
|
|
158
|
+
| C · CHECK / BET SMALL / MEDIUM / LARGE / ALL-IN | 0% | 56% | 33% |
|
|
159
|
+
| D · hierarchical | 0% | 44% | 100% |
|
|
160
|
+
|
|
161
|
+
Jev's aggressive-selection rate collapsed from 100% to 33% purely by adding
|
|
162
|
+
almost-identical bet-size classes, and the hierarchical architecture restored
|
|
163
|
+
it to 100%. This is the option-count asymmetry the release removes. Jev's mean
|
|
164
|
+
family aggressive probability mass rose from 45% (fragmented flat) to 56%
|
|
165
|
+
(hierarchical), and its selected-family probability from 37% to 73%.
|
|
166
|
+
|
|
167
|
+
Strict diagnostics were preserved: Layer A (action-id mapping) 100%, Layer D
|
|
168
|
+
(strictly dominated) 100% flat / 94% hierarchical, Layer B (raw hand
|
|
169
|
+
recognition) 62% — the same cross-model weakness as before, and the reason
|
|
170
|
+
Strategy mode exists.
|
|
171
|
+
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# pokertools-arena 0.3.x — technical verification record
|
|
2
|
+
|
|
3
|
+
Fairness/correctness release: hierarchical decisions for every model, Strategy/Raw
|
|
4
|
+
benchmark modes, deterministic hand evaluation, and a prose-free primary decision
|
|
5
|
+
contract.
|
|
6
|
+
|
|
7
|
+
## 1. Implementation summary
|
|
8
|
+
|
|
9
|
+
### Canonical decision core (`src/lib/decision-core.js`)
|
|
10
|
+
|
|
11
|
+
- `legalActionFamilies(legalActions, { toCall })` — the one canonical family set.
|
|
12
|
+
Free check exposes `check` + (`bet`|`raise`); facing a bet exposes
|
|
13
|
+
`fold`/`call`/(`raise`). Never exposes `fold` when `check` is free; never
|
|
14
|
+
exposes `bet` and `raise` together; throws on any arena invariant violation.
|
|
15
|
+
- `planAggressiveSizes` / `aggressiveSizesForState` / `legalAggressiveSizes` —
|
|
16
|
+
deterministic, engine-validated sizing. At most four buckets
|
|
17
|
+
(`small` ≈33% pot, `medium` ≈67%, `large` ≈100%, `all_in`), clamped to the
|
|
18
|
+
stack, respecting the minimum bet/raise, deduplicated, near-duplicates removed,
|
|
19
|
+
all-in only when materially distinct, correct for short stacks.
|
|
20
|
+
- `buildHierarchicalDecision`, `familyCriteria`, `sizeCriteria`,
|
|
21
|
+
`chatFamilySchema`, `chatSizeSchema`, `buildJevFamilyQuestions`,
|
|
22
|
+
`buildJevSizeQuestions` — one representation of the hierarchy for every adapter.
|
|
23
|
+
- `resolveHierarchicalAction` — resolves `(family, size)` to one validated
|
|
24
|
+
engine action; the deterministic size amount is authoritative.
|
|
25
|
+
- `decideHierarchical` — the two-stage executor: one shared `combineAbort`
|
|
26
|
+
signal (one action clock), family call then optional size call, no prose,
|
|
27
|
+
flat menus stripped from hierarchical prompts, request/usage accounting.
|
|
28
|
+
- `applyBenchmarkMode` / `stripHeroHand` — Strategy adds a deterministic,
|
|
29
|
+
hero-only `heroHand`; Raw withholds it.
|
|
30
|
+
- `renderDecisionState` — `canonical_json` / `compact_json` / `markdown`.
|
|
31
|
+
- `aggregateActionProbabilitiesByFamily` / `probabilityStats` — legacy flat
|
|
32
|
+
probability folding into family mass, plus entropy/top-second-gap stats.
|
|
33
|
+
- `diagnosticsCounters` — process-local HTTP request/retry/rate-limit counters.
|
|
34
|
+
|
|
35
|
+
### Production (`src/app.js`)
|
|
36
|
+
|
|
37
|
+
- Hierarchical is the production default; flat legacy is diagnostics-only.
|
|
38
|
+
- Tournament-wide `benchmarkMode`, `decisionArchitecture`, `representation`,
|
|
39
|
+
`spectatorExplanations` settings, recorded in tournament meta, events and
|
|
40
|
+
replays.
|
|
41
|
+
- Deterministic engine-validated size set built once per decision and shared by
|
|
42
|
+
the request and the UI.
|
|
43
|
+
- Final action re-validated by PokerTools after both stages.
|
|
44
|
+
- Spectator explanations run after the move in an isolated serial queue with an
|
|
45
|
+
8-second budget, never feed model context, and never count toward primary
|
|
46
|
+
latency. Jev gets typed telemetry, never fabricated prose.
|
|
47
|
+
- `decisionMeta` carries `decisionArchitecture`, `family`, `sizing`,
|
|
48
|
+
`finalAction`; events carry `primaryDecisionLatencyMs`.
|
|
49
|
+
- Current Decision panel shows stage 1 (`Action family`, "Stage 1 of 2 · action")
|
|
50
|
+
and stage 2 (`BET size`, "Stage 2 of 2 · sizing") with identical geometry for
|
|
51
|
+
all models.
|
|
52
|
+
- Recent Decisions and replay show translated family/sizing telemetry; legacy
|
|
53
|
+
flat events show "Aggregated from legacy flat action probabilities" without
|
|
54
|
+
changing the historical action. Raw `A0/A1` identifiers are no longer shown in
|
|
55
|
+
the primary UI.
|
|
56
|
+
- New UI: Benchmark mode selector (Strategy/Raw), and a Diagnostics/developer
|
|
57
|
+
section for Decision architecture, Representation and Spectator explanations.
|
|
58
|
+
|
|
59
|
+
### Diagnostics and tests
|
|
60
|
+
|
|
61
|
+
- `tests/unit/hierarchical-decision-tests.mjs` — 16 offline fairness assertions.
|
|
62
|
+
- `tests/real/real-decision-diagnostics.mjs` — hierarchical strict diagnostics,
|
|
63
|
+
`action_fragmentation` (flat A/B/C vs hierarchical D), four-way probe matrix,
|
|
64
|
+
flat-vs-hierarchical and Jev family-telemetry tables, acceptance answers,
|
|
65
|
+
HTTP request accounting, `--from-json` offline re-render.
|
|
66
|
+
- `tests/real/real-tournament-ab.mjs` — real flat-vs-hierarchical tournament A/B.
|
|
67
|
+
- `tests/analysis/diagnostics-summary.mjs` — concise release report with `--merge`.
|
|
68
|
+
- `tools/release/archive.mjs` — source archive builder with secret/exclusion guards.
|
|
69
|
+
|
|
70
|
+
### Release
|
|
71
|
+
|
|
72
|
+
- Version `0.3.0` in `package.json` and `package-lock.json`.
|
|
73
|
+
- `RELEASE_NOTES.md`, `CONTEXT.md`, `THIRD_PARTY_NOTICES.md`, updated README and
|
|
74
|
+
`tests/README.md`, new `0.3.0` CSS section, release-check invariants.
|
|
75
|
+
|
|
76
|
+
## 2. Bugs found and fixed
|
|
77
|
+
|
|
78
|
+
1. **Jev hierarchical questions were double-wrapped.** `buildJevFamilyQuestions`
|
|
79
|
+
returned `{ action_family: { type: 'choice', … } }`, which the transport
|
|
80
|
+
wrapped again into `questions.action_family.action_family`; OpenRouter rejected
|
|
81
|
+
it (`invalid_union`, "No matching discriminator"). Fixed to return the bare
|
|
82
|
+
choice object.
|
|
83
|
+
2. **Hierarchical sizing lost the deterministic amount.** `resolveHierarchicalAction`
|
|
84
|
+
fell back to the nearest flat candidate when the exact amount was absent,
|
|
85
|
+
silently replacing e.g. a 33%-pot size with a different amount. Fixed so the
|
|
86
|
+
validated deterministic size amount is authoritative.
|
|
87
|
+
3. **`player` was out of scope** in the production final-validation path; changed
|
|
88
|
+
to `agent.id`.
|
|
89
|
+
4. **Flat menus leaked into hierarchical prompts**, giving Jev both the family
|
|
90
|
+
question and a flat action list. Fixed by stripping `legalActions` from
|
|
91
|
+
hierarchical state and text.
|
|
92
|
+
5. **Legacy flat-event aggregation required typed saved actions.** Added
|
|
93
|
+
description-based inference so old logs aggregate correctly.
|
|
94
|
+
6. **Tournament A/B `playersRemaining` mismatch** — the harness counted only
|
|
95
|
+
live stacks while production counts still-seated players; fixed to match.
|
|
96
|
+
7. **Tournament A/B percentage formatting** double-multiplied already-fractional
|
|
97
|
+
values; fixed with a value formatter and proper denominator counts.
|
|
98
|
+
8. **Offline re-render wiped transport counters**; fixed to preserve saved
|
|
99
|
+
counters (and the saved run files were restored).
|
|
100
|
+
|
|
101
|
+
## 3. Before/after diagnostic results
|
|
102
|
+
|
|
103
|
+
Broad run: 369 decisions, 415 HTTP requests, 0 request errors.
|
|
104
|
+
|
|
105
|
+
| Metric | Before (flat) | After (hierarchical) |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| Layer A action-id mapping | 100% | n/a (flat-only by design) |
|
|
108
|
+
| Layer B hand recognition | 62% | 62% (unchanged, cross-model weakness) |
|
|
109
|
+
| Layer D strictly dominated | 100% | 94% |
|
|
110
|
+
| Strict overall | 83% | 94% |
|
|
111
|
+
| Jev family entropy (bits) | 0.34 | 0.80 |
|
|
112
|
+
| Jev selected-family probability | 37% | 73% |
|
|
113
|
+
| Jev mean aggressive family mass | 45% | 56% |
|
|
114
|
+
|
|
115
|
+
## 4. Flat vs hierarchical comparison
|
|
116
|
+
On identical shared action scenarios (fragmentation/probe menus excluded):
|
|
117
|
+
|
|
118
|
+
| Model | Architecture | Agg rate | Check rate | Mean agg family mass | Mean latency |
|
|
119
|
+
| --- | --- | --- | --- | --- | --- |
|
|
120
|
+
| Gemma | Flat | 40% | 20% | — | 1583ms |
|
|
121
|
+
| Gemma | Hierarchical | 40% | 20% | — | 2176ms |
|
|
122
|
+
| Qwen | Flat | 40% | 20% | — | 7197ms |
|
|
123
|
+
| Qwen | Hierarchical | 33% | 27% | — | 5663ms |
|
|
124
|
+
| Jev | Flat | 40% | 20% | 40% | 407ms |
|
|
125
|
+
| Jev | Hierarchical | 60% | 0% | 46% | 777ms |
|
|
126
|
+
|
|
127
|
+
### Fragmentation experiment (aggressive-selection rate by menu)
|
|
128
|
+
|
|
129
|
+
| Menu | Gemma | Qwen | Jev |
|
|
130
|
+
| --- | --- | --- | --- |
|
|
131
|
+
| A · CHECK / BET ALL-IN | 0% | 89% | 100% |
|
|
132
|
+
| B · CHECK / BET SMALL / ALL-IN | 0% | 56% | 56% |
|
|
133
|
+
| C · CHECK / BET SMALL / MEDIUM / LARGE / ALL-IN | 0% | 56% | 33% |
|
|
134
|
+
| D · hierarchical | 0% | 44% | 100% |
|
|
135
|
+
|
|
136
|
+
Jev's aggression collapsed from 100% to 33% purely by adding almost-identical
|
|
137
|
+
bet-size classes, and hierarchy restored it to 100%.
|
|
138
|
+
|
|
139
|
+
### Real tournament A/B (12 hands/arch, 10000 stack, 50/100 blinds, 5 hands/level, ×1.5)
|
|
140
|
+
|
|
141
|
+
| Model | Architecture | Decisions | Agg opps | Aggressive | Agg rate |
|
|
142
|
+
| --- | --- | --- | --- | --- | --- |
|
|
143
|
+
| Gemma | Flat | 36 | 36 | 6 | 17% |
|
|
144
|
+
| Gemma | Hierarchical | 26 | 23 | 11 | 48% |
|
|
145
|
+
| Qwen | Flat | 35 | 34 | 13 | 38% |
|
|
146
|
+
| Qwen | Hierarchical | 12 | 12 | 2 | 17% |
|
|
147
|
+
| Jev | Flat | 30 | 30 | 2 | 7% |
|
|
148
|
+
| Jev | Hierarchical | 24 | 21 | 6 | 29% |
|
|
149
|
+
|
|
150
|
+
163 tournament decisions, 0 errors, 0 fallbacks. Win rate is not a conclusion at
|
|
151
|
+
this sample size.
|
|
152
|
+
|
|
153
|
+
### Representation sensitivity (accuracy on the fixed-answer action scenarios)
|
|
154
|
+
|
|
155
|
+
All representations scored 100% for all three models, with aggressive rate
|
|
156
|
+
varying only within small-sample noise (Gemma 20–40%, Qwen 20–40%, Jev 20–40%).
|
|
157
|
+
No provider needs a richer representation: canonical JSON, minimal JSON, flat
|
|
158
|
+
JSON, raw text, markdown, semantic keys and canonical+heroHand all carry the
|
|
159
|
+
same semantics.
|
|
160
|
+
|
|
161
|
+
### Context ablation
|
|
162
|
+
|
|
163
|
+
Accuracy was 100% in every context variant for every model. Jev's aggressive
|
|
164
|
+
rate stayed at 100% for immediate/action-history/public-stats/full context and
|
|
165
|
+
50% for recent-hands (small sample); no context field is required for legality
|
|
166
|
+
and none is provider-specific.
|
|
167
|
+
|
|
168
|
+
## 5. Fairness verification
|
|
169
|
+
|
|
170
|
+
Automated (`npm run test:hierarchical`, 16 assertions) plus `decision-diagnostics`:
|
|
171
|
+
|
|
172
|
+
1. Same canonical decision semantics for all models.
|
|
173
|
+
2. Identical action families for the same engine state.
|
|
174
|
+
3. Identical sizing candidates for the same engine state.
|
|
175
|
+
4. Strategy/Raw mode applies globally, never per seat.
|
|
176
|
+
5. `heroHand` is deterministic.
|
|
177
|
+
6. `heroHand` describes the hero only.
|
|
178
|
+
7. Opponent cards remain hidden.
|
|
179
|
+
8. Opponent hand evaluation is never exposed.
|
|
180
|
+
9. `recentHands`/`publicPlayerStats` equivalent across adapters.
|
|
181
|
+
10. Jev criteria exactly match the chat schema enums.
|
|
182
|
+
11. Chat models cannot select a size not presented to Jev.
|
|
183
|
+
12. Jev cannot select a size not presented to chat models.
|
|
184
|
+
13. Final engine actions are validated after both stages.
|
|
185
|
+
14. Free `CHECK` never exposes `FOLD`.
|
|
186
|
+
15. The total action timer spans both hierarchical stages (one abort signal).
|
|
187
|
+
16. Spectator explanations cannot affect future model context.
|
|
188
|
+
17. Spectator explanation latency is not counted as primary action latency.
|
|
189
|
+
18. Diagnostic perfect-information fixtures cannot enter tournament mode.
|
|
190
|
+
|
|
191
|
+
## 6. Jev diagnosis after fragmentation is removed
|
|
192
|
+
|
|
193
|
+
- Fragmentation, not poker policy, drove most of the observed passivity. The
|
|
194
|
+
two-choice menu produced 100% aggression; five aggressive classes produced 33%;
|
|
195
|
+
hierarchy restored 100% on the same states.
|
|
196
|
+
- Jev's aggressive probability mass was always substantial but fragmented
|
|
197
|
+
(flat family entropy 0.34, selected-family probability 37%). Hierarchy raised
|
|
198
|
+
selected-family probability to 73% and entropy to 0.80.
|
|
199
|
+
- On the 20-rep river probe, Jev bets the wheel straight 100% of the time with
|
|
200
|
+
hierarchy (both raw and heroHand), with mean aggressive family mass 61–82%.
|
|
201
|
+
- Residual passivity (e.g. flat 7% aggressive rate in one real tournament) is
|
|
202
|
+
best explained by the model's own policy and by raw-card hand recognition, not
|
|
203
|
+
by the option list. The arena now presents identical, symmetric choices to
|
|
204
|
+
every model.
|
|
205
|
+
|
|
206
|
+
## 7. Exact commands run
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm install
|
|
210
|
+
npm test
|
|
211
|
+
node tests/real/real-decision-diagnostics.mjs --experiments strict,hierarchical,fragmentation,probe --reps 3 --cap 380 --delay 50 --out logs/release-0.3.0
|
|
212
|
+
node tests/real/real-decision-diagnostics.mjs --experiments probe --reps 20 --cap 250 --delay 50 --out logs/release-0.3.0
|
|
213
|
+
node tests/real/real-decision-diagnostics.mjs --experiments representations,context,herohand --reps 2 --cap 320 --delay 50 --out logs/release-0.3.0
|
|
214
|
+
node tests/real/real-tournament-ab.mjs --hands 12 --cap 150 --models gemma,qwen,jev --architecture flat,hierarchical --delay 60 --out logs/release-0.3.0
|
|
215
|
+
node tests/analysis/diagnostics-summary.mjs --dir logs/release-0.3.0 --diagnostics <broad.json> --merge <probe.json> --tournament <ab.json> --out logs/release-0.3.0/diagnostics-summary.md
|
|
216
|
+
node tools/release/archive.mjs --out pokertools-arena-0.3.0.tar.gz
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 8. npm test result
|
|
220
|
+
|
|
221
|
+
`npm test` passes: build, release-check, `.env` bootstrap, `@pokertools/engine`
|
|
222
|
+
integration, decision scenarios, offline diagnostics, and the new hierarchical
|
|
223
|
+
fairness suite.
|
|
224
|
+
|
|
225
|
+
## 9. Real API request/error counts
|
|
226
|
+
|
|
227
|
+
- Broad run: 369 decisions, 415 HTTP requests, 0 errors (1 retry, 1 rate-limit event).
|
|
228
|
+
- Probe (20 reps): 240 decisions, 323 HTTP requests, 0 errors.
|
|
229
|
+
- Representation/context/heroHand run: recorded in its own report.
|
|
230
|
+
- Tournament A/B: 163 decisions, 0 errors, 0 fallbacks.
|
|
231
|
+
- Total: >750 real decisions, 0 decision errors.
|
|
232
|
+
|
|
233
|
+
## 10. Remaining caveats
|
|
234
|
+
|
|
235
|
+
- Layer B (raw hand recognition) remains 62% for all models; Strategy mode exists
|
|
236
|
+
precisely because of this, and Raw mode keeps the recognition benchmark alive.
|
|
237
|
+
- Hierarchical Layer D was 94% vs 100% flat on 36 samples (Qwen bet a smaller
|
|
238
|
+
value size or checked in two synthetic always-calls spots); this is a model
|
|
239
|
+
behavior, not an arena legality issue, and the sample is small.
|
|
240
|
+
- Win rate is not used as a conclusion at these sample sizes.
|
|
241
|
+
- The tournament A/B uses the same action clock but hierarchical aggressive
|
|
242
|
+
decisions require two calls; observed latencies are reported rather than
|
|
243
|
+
normalized.
|
|
244
|
+
|
|
245
|
+
## 11. Release archive
|
|
246
|
+
|
|
247
|
+
`pokertools-arena-0.3.0.tar.gz` — full source archive (source, tests,
|
|
248
|
+
diagnostics, bin, browser shims, GitHub workflows, `package.json`, lockfile,
|
|
249
|
+
documentation). Excludes `.env`, secrets, generated logs, `dist/`, and
|
|
250
|
+
`node_modules`. Verified: 49 entries, no real `.env`, no API key.
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pokertools-arena",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Browser-first AI poker benchmark powered by @pokertools/engine with generic OpenAI-compatible endpoints, optional OpenRouter/Jev Decisions routing, deterministic hierarchical decisions, paired fixed-state methodology, and reproducible reporting.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pokertools-arena": "bin/pokertools-arena.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"dist/",
|
|
12
|
+
"src/",
|
|
13
|
+
"docs/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
".env.example"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node bin/pokertools-arena.mjs",
|
|
20
|
+
"start:chrome": "node bin/pokertools-arena.mjs --chrome",
|
|
21
|
+
"build": "node build.mjs",
|
|
22
|
+
"og": "rsvg-convert -w 1200 -h 630 -o src/assets/og-image.png src/assets/og-image.svg",
|
|
23
|
+
"check": "node --check src/app.js && node --check src/benchmark/scenarios.js && node --check build.mjs && node --check bin/pokertools-arena.mjs && node tests/release-check.mjs",
|
|
24
|
+
"test": "npm run build && npm run check && npm run test:unit && npm run test:integration",
|
|
25
|
+
"test:unit": "node tests/unit/decision-scenarios.mjs && node tests/unit/decision-diagnostics.mjs && node tests/unit/hierarchical-decision-tests.mjs && node tests/unit/methodology-tests.mjs && node tests/unit/size-bucket-tests.mjs && node tests/unit/paired-architecture-tests.mjs && node tests/unit/archive-content.mjs && node tests/unit/report-consistency.mjs",
|
|
26
|
+
"test:integration": "node tests/integration/pokertools-integration.mjs && node tests/integration/env-bootstrap.mjs",
|
|
27
|
+
"test:diagnostics": "node tests/real/real-decision-diagnostics.mjs",
|
|
28
|
+
"test:diagnostics:dry": "node tests/real/real-decision-diagnostics.mjs --dry-run",
|
|
29
|
+
"test:real": "node tests/real/real-decision-diagnostics.mjs && node tests/real/real-paired-corpus.mjs",
|
|
30
|
+
"test:tournament-ab": "node tests/real/real-tournament-ab.mjs",
|
|
31
|
+
"test:engine": "node tests/integration/pokertools-integration.mjs",
|
|
32
|
+
"test:env": "node tests/integration/env-bootstrap.mjs",
|
|
33
|
+
"test:scenarios": "node tests/unit/decision-scenarios.mjs",
|
|
34
|
+
"test:diagnostics:offline": "node tests/unit/decision-diagnostics.mjs",
|
|
35
|
+
"test:hierarchical": "node tests/unit/hierarchical-decision-tests.mjs",
|
|
36
|
+
"test:tournament": "node tests/real/real-tournament-ab.mjs",
|
|
37
|
+
"report:diagnostics": "node tests/analysis/release-report.mjs",
|
|
38
|
+
"report:release": "node tests/analysis/release-report.mjs --docs",
|
|
39
|
+
"analyze:decisions": "node tests/analysis/diagnostics-analyze.mjs",
|
|
40
|
+
"summarize": "node tests/analysis/diagnostics-summary.mjs",
|
|
41
|
+
"release:archive": "node tools/release/archive.mjs",
|
|
42
|
+
"test:browser": "node bin/pokertools-arena.mjs --chrome",
|
|
43
|
+
"prepack": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"poker",
|
|
47
|
+
"llm",
|
|
48
|
+
"benchmark",
|
|
49
|
+
"openrouter",
|
|
50
|
+
"openai",
|
|
51
|
+
"jev",
|
|
52
|
+
"pokertools",
|
|
53
|
+
"ai-agents"
|
|
54
|
+
],
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=24.0.0"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "git+https://github.com/pokertools-arena/pokertools-arena.github.io.git"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://pokertools-arena.github.io/",
|
|
67
|
+
"bugs": {
|
|
68
|
+
"url": "https://github.com/pokertools-arena/pokertools-arena.github.io/issues"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@pokertools/engine": "1.0.20",
|
|
72
|
+
"@pokertools/evaluator": "1.0.20"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"esbuild": "^0.28.2"
|
|
76
|
+
}
|
|
77
|
+
}
|