{
+ games_ground::edition::actions()
+ .ok()?
+ .into_iter()
+ .find(|c| c.id.to_uppercase().contains("GROUND"))
+ .map(|c| c.rules_text)
+}
+
/// A GROUND sub-choice, in words, naming whoever it touches.
fn ground_choice_label(c: &games_ground::GroundChoice) -> String {
use games_ground::GroundChoice as G;
@@ -1605,6 +1663,23 @@ fn move_section(
);
}
}
+ // CB-WP-0045: when a GROUND mode is on offer, the card's own
+ // account of the three modes travels with the decision. By the
+ // Reveal step the action card has left the screen, so the player
+ // was choosing GR / OU / ND from three names alone.
+ if legal
+ .iter()
+ .any(|c| matches!(c, games_ground::GroundCommand::ChooseGroundMode { .. }))
+ {
+ if let Some(text) = ground_modes_text() {
+ let _ = write!(
+ s,
+ "what the GROUND modes do\
+
{} ",
+ esc(&text)
+ );
+ }
+ }
s.push_str("");
for (i, c) in legal.iter().enumerate() {
let spatial = seat.is_some_and(|seat| crate::input::affordance(c, seat).is_some());
diff --git a/crates/cb-render-html/src/lib.rs b/crates/cb-render-html/src/lib.rs
index 0172281..02b60de 100644
--- a/crates/cb-render-html/src/lib.rs
+++ b/crates/cb-render-html/src/lib.rs
@@ -697,6 +697,114 @@ mod gamelog {
}
}
+ /// **The number is on the card** (CB-WP-0045).
+ ///
+ /// Every move label says "Problem 7"; nothing on the table said which
+ /// card that was. In the old row, position was a hint — once
+ /// Problems are placed by scope the row is gone and the hint with it.
+ /// The number was only ever a fallback for a missing title, so it
+ /// appeared exactly when it was least useful.
+ #[test]
+ fn every_problem_card_shows_its_number() {
+ let v = crate::testfix::view(Some(PlayerId(0)));
+ let text = crate::text_of(&crate::doc::document(
+ &v,
+ &[],
+ "/c",
+ Some(PlayerId(0)),
+ false,
+ ));
+ for key in v.problems.keys() {
+ assert!(
+ text.contains(&format!("{key}")),
+ "Problem {key} is on the table with no number, and the move \
+ labels call it by one"
+ );
+ }
+ }
+
+ /// **The game's own words for a DARVO stage** (CB-WP-0045).
+ ///
+ /// Reported as *"the GROUND and DARVO logic is far from self
+ /// explanatory"*. `DARVO.csv` carries `mandatory_effect` per stage —
+ /// vendored for tripwires and never shown, so the page said "DARVO
+ /// Reverse" and left the player to guess. F18's shape again: the data
+ /// was present and could not fire.
+ #[test]
+ fn a_darvo_stage_says_what_it_does() {
+ let v = crate::testfix::view(Some(PlayerId(0)));
+ let text = crate::text_of(&crate::doc::document(
+ &v,
+ &[],
+ "/c",
+ Some(PlayerId(0)),
+ false,
+ ));
+ // The fixture has a seat at Reverse.
+ assert!(
+ text.contains("what Reverse does"),
+ "a seat in a DARVO stage does not say what the stage does"
+ );
+ // And it is the EDITION's sentence, not a paraphrase of ours.
+ let edition = games_ground::edition::darvo_stages()
+ .expect("DARVO.csv")
+ .into_iter()
+ .find(|s| s.stage == "REVERSE")
+ .expect("REVERSE");
+ let head: String = edition.mandatory_effect.chars().take(40).collect();
+ assert!(
+ text.contains(&head),
+ "the stage text is not the edition's own words: {head:?}"
+ );
+ }
+
+ /// **The GROUND modes explain themselves at the decision** (CB-WP-0045).
+ ///
+ /// By the Reveal step the action card has left the screen, so the
+ /// player was choosing GR / OU / ND from three names alone.
+ #[test]
+ fn the_ground_modes_explain_themselves_where_they_are_chosen() {
+ use games_ground::{GroundCommand, GroundMode};
+ let v = crate::testfix::view(Some(PlayerId(0)));
+ let legal = vec![GroundCommand::ChooseGroundMode {
+ mode: GroundMode::Gr,
+ choice: None,
+ }];
+ let with = crate::text_of(&document_with_log(
+ &v,
+ &legal,
+ crate::TEST_ENDPOINTS,
+ Some(PlayerId(0)),
+ false,
+ Account::of(&[]),
+ &[],
+ ));
+ assert!(
+ with.contains("what the GROUND modes do"),
+ "the mode choice is offered with no account of the modes"
+ );
+ assert!(
+ with.contains("Ground & Restate"),
+ "the explanation is not the card's own text"
+ );
+
+ // Absent when no mode is on offer — an always-on wall of rules is
+ // how a player stops reading them.
+ let without = crate::text_of(&document_with_log(
+ &v,
+ &[],
+ crate::TEST_ENDPOINTS,
+ Some(PlayerId(0)),
+ false,
+ Account::of(&[]),
+ &[],
+ ));
+ assert!(
+ !without.contains("what the GROUND modes do"),
+ "the mode explanation shows when no mode is being chosen"
+ );
+ }
+
/// **A Problem is drawn where its Stress lands** (CB-WP-0043).
///
/// H2 scopes End-of-Round Stress, so a row across the middle — true
diff --git a/trials/2026-08-08-1818-2.yaml b/trials/2026-08-08-1818-2.yaml
new file mode 100644
index 0000000..c03c081
--- /dev/null
+++ b/trials/2026-08-08-1818-2.yaml
@@ -0,0 +1,158 @@
+scenario: ground/cb-play-session
+description: recorded by cb-play (CB-WP-0008 T02)
+covers: []
+provisional: false
+provisional_owner: ''
+provisional_raised: ''
+ruled: ''
+ruled_by: ''
+ruled_note: ''
+encodes_u_item: ''
+seed: 2
+setup:
+ players: 3
+ preset: standard-3p
+ patch: {}
+commands:
+- actor: P1
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 1
+- actor: P3
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 1
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 3
+- actor: P3
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 2
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SUPPORT
+ target: P2
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P2
+ cmd: respond_to_support
+ args:
+ response: accept_bond
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 3
+- actor: P2
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 4
+- actor: P3
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 4
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SUPPORT
+ target: P3
+- actor: P2
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 4
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: respond_to_support
+ args:
+ response: accept_bond
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+expect:
+ events: []
+ state: {}
+ rejects: []
+ state_hash: ff50bedd79bef9ac80f3481d56a3b50b1f3cdef7d265b831ca17cdadbdf49e86
diff --git a/trials/2026-08-08-1818-3.yaml b/trials/2026-08-08-1818-3.yaml
new file mode 100644
index 0000000..d57664e
--- /dev/null
+++ b/trials/2026-08-08-1818-3.yaml
@@ -0,0 +1,187 @@
+scenario: ground/cb-play-session
+description: recorded by cb-play (CB-WP-0008 T02)
+covers: []
+provisional: false
+provisional_owner: ''
+provisional_raised: ''
+ruled: ''
+ruled_by: ''
+ruled_note: ''
+encodes_u_item: ''
+seed: 3
+setup:
+ players: 3
+ preset: standard-3p
+ patch: {}
+commands:
+- actor: P1
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 2
+- actor: P3
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 2
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: choose_ground_mode
+ args:
+ choice: protect_problem
+ mode: OU
+ problem: 1
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P1
+ cmd: choose_darvo_target
+ args:
+ problem: 1
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P1
+ cmd: choose_darvo_target
+ args:
+ problem: 2
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+expect:
+ events: []
+ state: {}
+ rejects: []
+ state_hash: 1c5d6828619d00851bfd6048082074044a7343d606df4062f11d696f05fbc988
diff --git a/trials/2026-08-08-1818.md b/trials/2026-08-08-1818.md
new file mode 100644
index 0000000..847a21d
--- /dev/null
+++ b/trials/2026-08-08-1818.md
@@ -0,0 +1,19 @@
+# Trial log
+
+What the player said while playing, bound to the position they said it at
+(CB-WP-0027, ADR-0014). The recording beside this file is the position;
+`state_hash` is what reaches it.
+
+**These are raw notes and they stay here.** Nothing in this file travels to
+`ground-game` (ADR-0014 D4) — a note reaches them only by being promoted to a
+register finding, by a human, with the wording chosen then.
+
+
+
+| n | game | after | round | step | state_hash | comment |
+|---|---|---|---|---|---|---|
+| 1 | 1 | 36 | 5 | after the end | 73661a4ab998 | Ok, that seemed more interesting and we lost. I am not sure how ground actually works. lets try again. |
+| 2 | 2 | 35 | 5 | after the end | ff50bedd79be | We did it this time. |
+| 3 | 3 | 44 | 5 | after the end | 1c5d6828619d | The GROUND and DARVO logic is far from self explanatory. That needs to be improved. |
+
+
diff --git a/trials/2026-08-08-1818.yaml b/trials/2026-08-08-1818.yaml
new file mode 100644
index 0000000..e76c88d
--- /dev/null
+++ b/trials/2026-08-08-1818.yaml
@@ -0,0 +1,160 @@
+scenario: ground/cb-play-session
+description: recorded by cb-play (CB-WP-0008 T02)
+covers: []
+provisional: false
+provisional_owner: ''
+provisional_raised: ''
+ruled: ''
+ruled_by: ''
+ruled_note: ''
+encodes_u_item: ''
+seed: 1
+setup:
+ players: 3
+ preset: standard-3p
+ patch: {}
+commands:
+- actor: P1
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 1
+- actor: P3
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 1
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 4
+- actor: P2
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 3
+- actor: P3
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 3
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: SUPPORT
+ target: P1
+- actor: P3
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 2
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: respond_to_support
+ args:
+ response: accept_bond
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+- actor: P1
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 3
+- actor: P2
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: P3
+ cmd: select_action
+ args:
+ action: GROUND
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P2
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: P3
+ cmd: choose_ground_mode
+ args:
+ mode: GR
+- actor: SYSTEM
+ cmd: resolve
+ args: {}
+- actor: SYSTEM
+ cmd: end_round
+ args: {}
+expect:
+ events: []
+ state: {}
+ rejects: []
+ state_hash: 73661a4ab99852cd098bed5fd34ad08b6999195a8b0fe0f8a0f695e5cac1c1b4
diff --git a/workplans/CB-WP-0045-the-table-names-what-it-shows.md b/workplans/CB-WP-0045-the-table-names-what-it-shows.md
new file mode 100644
index 0000000..435df4c
--- /dev/null
+++ b/workplans/CB-WP-0045-the-table-names-what-it-shows.md
@@ -0,0 +1,102 @@
+---
+id: CB-WP-0045
+kind: product
+title: "The table names what it shows"
+status: done
+---
+
+# Purpose
+
+```
+structural tier S (rendering only -- no rule, no dependency, no budget,
+ no artifact contract moved)
+declared tier S
+```
+
+First declaration after [ADR-0021](../decisions/ADR-0021-the-chaos-roll-is-retired.md)
+retired the chaos roll.
+
+## The reports
+
+Three sessions, `make trials` (notes now bind per game — ADR-0019 holds:
+`g1+0`, `g2+35`, `g3+44`):
+
+> *"Ok, that seemed more interesting and we lost."* / *"We did it this
+> time."* — 2026-08-08 1818
+
+The H2 dynamics land. Two things do not:
+
+> *"The GROUND and DARVO logic is far from self explanatory. That needs to
+> be improved."* — 2026-08-08 1818
+
+> *"As the cards are now located differently on the table i cant tell which
+> is problem 1, which 2 and so on."*
+
+## The numbering defect [CB-WP-0043] caused
+
+Every move label says **"Problem 7"**. The number was rendered on the card
+only as a **fallback for a missing title** — so it appeared exactly when
+it carried least information, and was absent whenever the card was
+identifiable enough to be worth pointing at.
+
+That was survivable while Problems sat in one ordered row: **position was
+the number**. [CB-WP-0043](CB-WP-0043-a-problem-sits-where-its-stress-lands.md)
+scattered them by scope, deleted the row, and took the implicit index with
+it — without anything going red, because no test named the number.
+
+**A rendering change removed a fact the page had been carrying
+positionally.** Not the ADR-0018 family (nothing computes the wrong
+subject); the nearer relative is CB-WP-0024 — a fact held by presentation
+alone does not survive the presentation changing.
+
+## The legibility defect: vendored and never shown
+
+`DARVO.csv` carries `mandatory_effect` per stage. `Actions.csv` carries
+`rules_text` for the three GROUND modes. **Both were vendored, both were
+used only as tripwires, and neither ever reached the player.** The seat
+panel said `DARVO Reverse`; the move buttons offered `GR` / `OU` / `ND`.
+
+**F18's shape once more**: the edition's own words were present in the
+repo and could not fire. The player was asked to choose between three
+modes at the point where the action card has already left the screen.
+
+## Task: put the number and the words on the table
+
+```task
+id: CB-WP-0045-T01
+status: done
+priority: high
+```
+
+**Controls:**
+
+- **every Problem on the table shows its number** — asserted over
+ `view.problems.keys()`, so a card that renders without one goes red
+ whatever the fixture holds;
+- **the explanation is the edition's sentence, not our paraphrase** —
+ the DARVO test re-reads `DARVO.csv` and asserts a prefix of
+ `mandatory_effect` appears verbatim (ADR-0015);
+- **the GROUND modes explain themselves where they are chosen** — present
+ when `ChooseGroundMode` is legal, and **absent when it is not**. An
+ always-on wall of rules is how a player stops reading them, so the
+ negative half is asserted too;
+- both explanations are ``, so the account can be closed.
+
+**Done 2026-08-08.** 78 tests pass; `make all` green; verified against a
+live `make ground VARIANT=h2` page — badges `1 2 3 4 …` and the header
+reading `rules h2-scoped-problem-stress`.
+
+## Not done here
+
+- **Nothing explains what a *scope does*.** H2 is the variation under
+ study, the marker now says *whose* Problem it is and where it sits — and
+ no text on the page says that a scoped Problem presses only the owner's
+ bond network. The player can see the placement and still not know the
+ rule it encodes. This is the largest remaining gap and it is a
+ `ground-game` question first: the scope rule is ours (Variant), not the
+ edition's, so there is no vendored sentence to render.
+- **The trial log header does not record the variant.** A note from an H2
+ session and a note from a baseline session are indistinguishable in the
+ log, which is a real problem now that both are being played.
+- **The `` are closed-by-default for DARVO, open for GROUND.**
+ Chosen, not measured; whether a player opens the closed one is untested.