--- id: CB-WP-0041 kind: product title: "The extensive-form foundation" status: active --- # Purpose ``` structural tier M (states what the kernel claims about itself as a game-theoretic object, and prepares — but does not build — a capability port) chaos d8 = 5 → no override declared tier M ``` **Declaration 1 of chaos window 4.** Window 3 closed at 12 declarations with one override that changed nothing, and **its verdict is still owed** ([`ChaosRollHistory.md`](../specs/ChaosRollHistory.md)). ## Why [CB-RES-0009](../research/CB-RES-0009-extensive-form-is-the-lingua-franca.md) found that the **extensive-form game is the interchange format** between describing a game and analysing it — Ludii's universality result grounds its language in EFGs, and OpenSpiel's CFR, best-response and exploitability consume them. **And that we already have most of one**, under other names: | EFG component | ours | |---|---| | histories | the journal — `Applied { actor, command, events }` in order | | actions | `bot::legal_commands(state, seat)` | | **information sets** | **`state.project(Viewer::Player(seat))`** | | payoffs | `Outcome` / `score()` | | terminal | `outcome.is_some()` | **This workplan does not build the port.** It answers what is true of the engine today, because a port built on an unchecked assumption is worse than no port — and one of the three gaps invalidates every equilibrium concept if it turns out badly. ## Task: does the engine satisfy perfect recall? ```task id: CB-WP-0041-T01 status: done priority: high ``` **Perfect recall** — every player remembers their own past actions and observations — is the assumption **CFR and exploitability both rest on**. Nobody has checked whether ours holds. Formally, for a seat `i` and any two histories `h`, `h'` in the same information set of `i`: the *sequence of `i`'s own actions and information sets* along `h` and `h'` must be identical. **Controls:** - **derived from the journal and the projection**, not asserted — the check must be able to say NO; - **a positive control**: a deliberately forgetful projection must fail it, or the check proves nothing; - **stated per seat count**, since the partition depends on how many seats there are to hide from; - **the answer may be that we do NOT have perfect recall**, and that is reported as plainly as the other outcome. It would be a real finding and would make Track B's adoption unsound as it stands. **Done 2026-08-08. The answer is "it depends what you call an information set", and the distinction is the result.** 44,938 decision points, random play, 2/3/4/6 seats. | reading | information set is… | violations | |---|---|---| | **A** | the seat's **current projection** — what `project(Viewer::Player(seat))` returns and the page renders | **22** | | **B** | the seat's **observation history** — every view seen and action taken, in order | **0** | **Reading A fails, and the witness is concrete**: two histories reach a byte-identical view — round 3, Select, same hand, same claimed Problem — where the seat had played `SOLVE, GROUND—OU(protect)` in one and `SUPPORT, SOLVE` in the other. **The view does not tell the seat what it did.** **The mechanism is that our state is a snapshot, not a history.** Selections clear each round and effects coincide, so a player cannot reconstruct their own past from the present. In a real game the player's memory supplies it; in the state, nothing does. **This is precisely OpenSpiel's `ObservationString` vs `InformationStateString` split**, arrived at here by measurement rather than by reading it off. `project()` is an *observation*. **So Track B is not closed — it is constrained**, and usefully: > **An extensive-form game built from this engine must key information > sets on observation histories, never on `project()`.** **Both directions are asserted.** Reading B empty, *and* Reading A non-empty — because if the sample stops finding Reading A violations the conclusion is unsupported and must be re-derived, not quietly kept. **What this cannot say.** It samples; it can falsify perfect recall and cannot establish it. Reading B's zero means *no counterexample was drawn*, which is weaker than "the property holds" and is printed as such. ## Task: say precisely what our chance is ```task id: CB-WP-0041-T02 status: todo priority: high ``` `setup` shuffles, deals and draws Lead from a seeded RNG, and the mid-game reshuffle derives from seed and round. **So a clay-borg game is one chance realisation, not a game with chance nodes**, and the panels approximate the distribution by sampling seeds. **Controls:** - **state it, do not fix it.** Monte Carlo over seeds is legitimate and is what we do; the defect would be calling it an EFG; - **name every point where chance enters**, from the code, not from memory; - **say what an explicit chance player would cost** — that is the input to T04's decision, and guessing it is how a port gets built on a hope. ## Task: state the simultaneity encoding, and check it ```task id: CB-WP-0041-T03 status: todo priority: medium ``` Commit/reveal **is** the textbook EFG encoding of simultaneous moves: sequence them, and hide the earlier move in an information set. **Control:** it is not enough to say so. **The projection must actually hide another seat's selection before Reveal**, and a test must fail if it stops doing that. That property is load-bearing for every claim in §1 of the research note and is currently only implied by `SelectionView::Hidden`. ## Task: decide whether to build the port at all ```task id: CB-WP-0041-T04 status: todo priority: medium ``` `decisions/ADR-*.md`, written **after** T01–T03 and not before. **It must be able to conclude "no".** Options include exporting an EFG, adopting OpenSpiel's API directly for analysis only, or deciding the gaps are too expensive and Track B borrows vocabulary rather than machinery. **Controls:** - **the decision cites T01's answer**, because if perfect recall fails, most of the option space closes; - **it states what it would cost to be wrong**; - **a port is declared separately, at its own tier.** Creating a capability port is a tier-L trigger and this workplan is M — it may not smuggle one in. ## Not in this workplan - **No EFG export, no OpenSpiel integration, no equilibrium computation.** - **No answer to "is exploitability meaningful for a co-operative game with a shared threshold"** — that is the other open question from CB-RES-0009 §6, it is a question about game theory rather than about our engine, and it wants its own pass.