171 lines
7.6 KiB
Markdown
171 lines
7.6 KiB
Markdown
---
|
||
id: CB-WP-0047
|
||
kind: product
|
||
title: "All four boards and all three modes"
|
||
status: done
|
||
state_hub_workstream_id: "772b2646-1258-409b-8480-6dedd6c5c894"
|
||
---
|
||
|
||
# Purpose
|
||
|
||
```
|
||
structural tier M (changes the state and therefore the recording, and
|
||
moves a reported number -- the threshold -- from the
|
||
engine to the edition)
|
||
declared tier M
|
||
```
|
||
|
||
> *"lets have a look at the cards and the rules to those cards again and
|
||
> implement all the scenarios and modes"*
|
||
|
||
## What was actually missing
|
||
|
||
The two halves turned out to be very different sizes.
|
||
|
||
**The modes were already implemented.** `ScoringMode`'s three arms all
|
||
score, `--mode` reaches them, F14 fixed the driver. What was missing was
|
||
that **nothing had ever compared them**, and the page never named them —
|
||
see below.
|
||
|
||
**The scenarios were not implemented at all.** `edition::deal` has taken a
|
||
`scenario_id` since the day it was written, and the only caller passed the
|
||
literal `"SCN_01"`:
|
||
|
||
```rust
|
||
let dealt = crate::edition::deal("SCN_01", seats)?;
|
||
```
|
||
|
||
**The seam was the whole mechanism and it sat unused.** 15 of the 20
|
||
Problem cards had never been dealt by anything — not a gate, not a panel,
|
||
not a player. Nothing was red, because nothing asked.
|
||
|
||
## Task: deal every board, name every mode
|
||
|
||
```task
|
||
id: CB-WP-0047-T01
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "d6298fa3-8c3d-4a47-ae52-945549b9ba95"
|
||
```
|
||
|
||
### The scenario is state
|
||
|
||
`GroundState.scenario`, `#[serde(default = "default_scenario")]` → `SCN_01`.
|
||
In the state for the same reason `variant` is (CB-WP-0038): the board and
|
||
the threshold follow from it, so a recording that did not carry it could
|
||
not be replayed.
|
||
|
||
**`standard-Np` keeps meaning `SCN_01`.** Twenty-six recorded scenarios
|
||
name that preset; a grammar that redefined it would have moved every one
|
||
of their boards while their hashes still claimed to pin them. `scn-03-4p`
|
||
names the rest. `--scenario` accepts `3`, `scn-03`, `SCN_03` or
|
||
`confidence` — ids are the edition's vocabulary, *"Broken Confidence"* is
|
||
the player's — and is validated against the edition, never a pattern, so
|
||
`SCN_09` is refused by name.
|
||
|
||
### The threshold now comes off the Scenario card
|
||
|
||
`threshold()` was `match seats { 0..=2 => 5, 3..=4 => 7, _ => 9 }` — the
|
||
engine's own copy of a number four cards already print, which is what F25
|
||
was raised about.
|
||
|
||
**The first version of this control was worthless and mutation said so.**
|
||
Reverting to the hardcoded bands left every test green, because all four
|
||
scenarios print 5/7/9 and the two paths are observationally identical on
|
||
every input the edition can supply. A control that cannot separate the
|
||
thing it is about from its fallback is not a control.
|
||
|
||
Fixed by splitting `threshold_from(list, scenario, seats)`, which can be
|
||
handed a card that **disagrees** (4/6/8). That test goes red under the
|
||
mutation; the loop over real scenarios never could.
|
||
|
||
### The page names the mode, the board, and what winning means
|
||
|
||
The header read `scoring CommonProblem` — the Rust variant's name, where
|
||
the Mode card is titled *"COMMON PROBLEM, PERSONAL EDGE"*. **CB-WP-0034
|
||
deleted this exact defect from the move buttons** and it was still
|
||
standing on the one line that says what winning means.
|
||
|
||
**And the coverage probe was holding it in place.** `("mode", "scoring
|
||
BondedCoalitions")` — a probe matching `Debug` output, which went red the
|
||
moment the defect was fixed. That is the **third** time this shape has
|
||
been found (CB-WP-0024, CB-WP-0034, here), and it is now specific enough
|
||
to state as a rule: *a probe that names a rendering pins that rendering;
|
||
probes name facts.*
|
||
|
||
The page now carries the Scenario's premise (*what happened*) and the Mode
|
||
card's rules text (*how this game is won*) — including the **tiebreak**,
|
||
which is a rule a player can play for: "lower Stress, then more Bonds"
|
||
changes what a losing seat should do in round five, and it lived in a
|
||
column nothing read.
|
||
|
||
### Mutations
|
||
|
||
| mutation | what went red |
|
||
|---|---|
|
||
| `setup` deals `"SCN_01"` regardless of preset | *"the four scenarios yield 1 distinct board"* |
|
||
| `threshold_from` ignores the card | *"the threshold did not come off the Scenario card"* |
|
||
| header back to `{mode:?}` | *"the page shows the Rust variant's name"* |
|
||
|
||
**Done 2026-08-08.** `make all` green. Verified live on
|
||
`make ground SCENARIO=4 MODE=coalitions`.
|
||
|
||
## What the panel found
|
||
|
||
`make panels` gained `scenario-panel`: 4 scenarios × 3 modes × 3 seat
|
||
bands, greedy throughout, 100 games per cell.
|
||
|
||
**1. Two of the four scenarios are the same board.** SCN_01 and SCN_02
|
||
have identical suits and values at every priority; every cell matches
|
||
exactly. Not a defect — a designed reskin is a legitimate choice — but
|
||
"four scenarios" buys **three** boards, and a panel that measured them as
|
||
four independent boards would be measuring one of them twice. Pinned by a
|
||
characterisation test so a future divergence is a decision, not a drift.
|
||
|
||
**2. SCN_04 is the hard board at 2 players** — 52% group success against
|
||
67% and 73%. It is the only deck that needs **two Repair** solutions in
|
||
the 2p deal, and the 2p Solution draw cannot always supply them. The seat
|
||
band the thresholds treat as uniform is not uniform across boards.
|
||
|
||
**3. The three modes produce identical play.** Group success is *exactly*
|
||
equal across all three modes in every cell. That is correct arithmetic and
|
||
a real finding: `GreedyPolicy` maximises the group outcome and never
|
||
consults `state.mode`, so **the two competitive modes are scoring lenses
|
||
over cooperative play**. `win/g` differs only by how the winner set is
|
||
carved from the same games. Whether COMMON PROBLEM and BONDED COALITIONS
|
||
change how the game is *played* is untested and cannot be tested by this
|
||
panel — it needs a policy that plays for personal score. Filed as F27.
|
||
|
||
**4. Every board is a formality at 6 players** — 100% group success in all
|
||
twelve cells. Consistent with F17's shape and with H2's motivation; noted,
|
||
not acted on.
|
||
|
||
## Not done here
|
||
|
||
- **No policy plays for personal score**, so F27 stands open and the two
|
||
competitive modes remain unexercised as *incentives*. This is the
|
||
largest remaining gap and it is the natural next pass.
|
||
- **SHARED GROUND's mastery rating counts CLAIMED CARDS, not points.**
|
||
The Mode card says *"All claimed Problem cards form one shared score …
|
||
subtract 1 for each Blame token still in play and 1 for each Denied
|
||
Problem"* — and the shared score is the claimed **value**, while
|
||
`mastery` subtracts from the claimed **count**. Underdetermined rather
|
||
than plainly wrong; raised for `ground-game` rather than changed here,
|
||
because scoring is theirs to rule on.
|
||
- **The trial log stamps the variant (CB-WP-0046) but not the scenario or
|
||
the mode.** A note from SCN_04 coalitions is indistinguishable from a
|
||
baseline SHARED GROUND note, which is the same defect one pass later in
|
||
two new axes. The marker already carries attributes, so this is small.
|
||
- **`design.py` reported an existing reproduction as absent.** It stats
|
||
`row["repro"]` directly, and the register writes **code spans** — so a
|
||
backticked file path was `os.path.exists("`games/…/x.rs`")` = False.
|
||
Named tests survived because `::` short-circuits before the stat, which
|
||
is why it had never shown. F27's panel existed and counted as debt
|
||
against a target of zero. ADR-0018's shape again: right computation,
|
||
wrong string. Fixed with three checks, including that stripping the
|
||
span must not turn the check off.
|
||
|
||
- **`catalog.yaml` and both `VARIANT.md`s were re-vendored in this pass**: `ground-game` moved H2
|
||
from `experimental` to `measured`, `decision: keep-as-experiment`,
|
||
citing our CB-EV-0032. That is their ruling on the previous pass, not
|
||
work done here.
|