Some checks failed
ci / check (push) Failing after 3s
T02 — all chance derives from one root seed. Three chance points, all reading it: the setup deck shuffle, the setup Lead draw, and the reshuffle permutation. The Problems deal is not chance at all. So in extensive-form terms the tree has a single chance node at the root. That test was wrong first, and the mutation caught it. It compared state hashes — and GroundState carries `seed` as a field, so "different seeds differ" was true by construction. Mutating the shuffle away left it green. It now compares the dealt configuration, and the same mutation fails it: a wrong-subject error inside the control written for T02. The reshuffle is a pure function of (seed, round) because K5 requires deterministic replay, where a real table reshuffles independently. That is a modelling restriction, not a defect, and it is now pinned. T03 — commit/reveal checked in both directions: before Reveal each seat sees its own selection and no other; after Reveal the information sets merge, because an encoding that hides forever is not commit/reveal either. T04 — ADR-0020 refuses the EFG port, and the blocker is T02 rather than T01, which inverts what the workplan expected. Perfect recall looked like the risk and is a constraint with a known answer: key on observation histories. Making chance explicit is the expensive one — the reshuffle would become a real chance node and break the K5 purity that every recording, replay bundle and trial-note hash depends on. A port would trade the property this project is built on for one it has never needed. Track B's first move is therefore a question, not a build: take "is exploitability meaningful for a co-operative game with a shared threshold" to OpenSpiel on a toy model, where answering it costs nothing. D4 states what being wrong looks like — OpenSpiel settling on a toy what three rounds of policy sweeps could not — and makes watching for it the next action. Taxonomy §4.1 records the EFG correspondence with the test that checks each row, so a later pass starts from a specification rather than a memory. Chaos window 4 at three declarations. Window 3's verdict is now two windows behind and should be evaluated rather than restated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
106 lines
5.2 KiB
Markdown
106 lines
5.2 KiB
Markdown
# ADR-0020: we do not build an extensive-form port, and here is what to do instead
|
||
|
||
status: accepted
|
||
date: 2026-08-08
|
||
decided by: agent, under the standing loop authorization
|
||
tier: M (a decision *not* to create a capability port; creating one would
|
||
have been L). chaos d8 = 7 → no override. **Declaration 3 of chaos
|
||
window 4.**
|
||
references: [CB-WP-0041](../workplans/CB-WP-0041-the-extensive-form-foundation.md),
|
||
[CB-RES-0009](../research/CB-RES-0009-extensive-form-is-the-lingua-franca.md),
|
||
[`specs/Positioning.md`](../specs/Positioning.md) §4 Track B,
|
||
[`simulators/openspiel.md`](../simulators/openspiel.md)
|
||
|
||
## Context
|
||
|
||
[CB-RES-0009](../research/CB-RES-0009-extensive-form-is-the-lingua-franca.md)
|
||
found the extensive-form game is the interchange format between
|
||
describing a game and analysing it, and that clay-borg already has most
|
||
of one. CB-WP-0041 T01–T03 then asked what is actually true of the
|
||
engine, and the answers changed the decision.
|
||
|
||
## D1 — the three findings this rests on
|
||
|
||
**T01. Perfect recall fails on the projection and holds on the history.**
|
||
22 violations in 44,938 sampled decision points when the information set
|
||
is taken to be `project(Viewer::Player(seat))`; none when it is the
|
||
seat's observation history. **`project()` is an observation, not an
|
||
information state** — OpenSpiel's own `ObservationString` /
|
||
`InformationStateString` split, found here by measurement.
|
||
|
||
**T02. All chance is one root node, and the reshuffle is correlated.**
|
||
Three chance points, all reading the root seed; the Problems deal is not
|
||
chance at all. The reshuffle permutation is a pure function of
|
||
`(seed, round)` **because K5 requires replay to be deterministic**, where
|
||
a real table would reshuffle independently.
|
||
|
||
**T03. Commit/reveal is the EFG simultaneity encoding**, and now checked
|
||
in both directions rather than assumed.
|
||
|
||
## D2 — we do not build an EFG export port
|
||
|
||
**Refused, and the reason is T02, not T01.**
|
||
|
||
T01 looked like the blocker and is not: it is a *constraint* with a known
|
||
answer — key information sets on observation histories. That is a
|
||
specification, and a cheap one.
|
||
|
||
**T02 is the blocker.** Making chance explicit means the reshuffle
|
||
becomes a genuine chance node, and **that breaks the purity that makes
|
||
every recording in this repo replay**. Our scenarios, the replay bundles,
|
||
`record.rs`'s round trip, the trial notes' state hashes — all of them rest
|
||
on chance being a function of state (GameKernel K5).
|
||
|
||
> **A port would trade the property this project is built on for a
|
||
> property it has never needed.** Replay is load-bearing for the evidence
|
||
> discipline; equilibrium computation is, so far, a research interest.
|
||
|
||
**And the port is not the cheap part anyway.** Enumeration is impossible —
|
||
24 Solution cards give 24! root branches — so any analysis samples chance,
|
||
which is what our seed sweeps already do.
|
||
|
||
## D3 — what we do instead, in order
|
||
|
||
1. **Write the mapping down, do not implement it.** The EFG correspondence
|
||
is now established and tested; it belongs in the taxonomy as a stated
|
||
relationship, so a later pass starts from a specification rather than a
|
||
memory.
|
||
2. **When Track B opens, take the question to OpenSpiel, not our kernel.**
|
||
Express a *small* GROUND-shaped game against its API and ask whether
|
||
exploitability says anything useful for a co-operative game with a
|
||
shared threshold. **That is a question about game theory, not about our
|
||
engine**, and answering it in our engine would cost the replay property
|
||
to learn something a toy model answers.
|
||
3. **If the answer is yes, revisit this ADR.** The cost in D2 is real but
|
||
it is not infinite: an EFG export that *samples* chance rather than
|
||
representing it would preserve K5 and might be enough.
|
||
|
||
## D4 — what this decision would look like if it were wrong
|
||
|
||
**If exploitability turns out to be the instrument that settles design
|
||
questions** — if it answers *"does ATTACK ever pay"* in a way three review
|
||
rounds of policy sweeps could not — then refusing the port will look like
|
||
protecting a process property at the cost of the product's purpose.
|
||
|
||
**The tell would be**: OpenSpiel answering, on a toy model, a question our
|
||
panels have been unable to answer. **Watch for it. That is D3 step 2, and
|
||
it is deliberately the next thing.**
|
||
|
||
## Consequences
|
||
|
||
- No EFG port, no OpenSpiel dependency, no equilibrium computation.
|
||
- **The AM-4 dependency budget is untouched**, which is worth stating
|
||
because adopting OpenSpiel would have been a large, non-Rust dependency.
|
||
- Track B's first move is a **question**, not a build.
|
||
- The three properties T01–T03 established are now regression-tested, so a
|
||
later port starts from checked ground rather than re-deriving them.
|
||
|
||
## What was rejected
|
||
|
||
| rejected | why |
|
||
|---|---|
|
||
| build an EFG export now | breaks K5 replay purity for a research interest (D2) |
|
||
| key information sets on `project()` | measured wrong — 22 perfect-recall violations (T01) |
|
||
| adopt OpenSpiel as an engine dependency | large non-Rust dependency, for a question a toy model answers |
|
||
| declare Track B blocked | it is not blocked, it is *specified*: the constraint is known and the next step is a question |
|
||
| say nothing and revisit later | the three findings would be re-derived from memory, which is how this project produces wrong-subject errors |
|