diff --git a/Makefile b/Makefile
index e97e91a..0d2db3a 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,10 @@ TOOLS := $(REPO)/tools
# reads the age from there (falling back to mtime).
TRIAL_NAME := $(shell date +%Y-%m-%d-%H%M)$(if $(SLUG),-$(SLUG),)
PLAYERS ?= 3
+# CB-WP-0044: which rules package to play. The page names it too --
+# a session that could not say which variant it was is what produced
+# the report "I did a session and noted no changes".
+VARIANT ?= ground-darvo-r0
PORT ?= 0
# Every cargo recipe runs at the repo root; the shell does not persist cd.
@@ -68,11 +72,13 @@ help:
## type in the notes panel is bound to the position you typed it at.
## `make ground PLAYERS=2 SLUG=darvo-confusion`
## Read the notes back afterwards with `make trials`.
+## play a session (VARIANT=ground-darvo-r0|h1|h2)
ground:
+ @echo " rules: $(VARIANT)"
@mkdir -p $(REPO)/trials
@echo " trial: trials/$(TRIAL_NAME).md (notes) + .yaml (the game)"
$(IN_REPO) $(CARGO) run -q -p cb-play -- \
- --players $(PLAYERS) --serve $(PORT) \
+ --players $(PLAYERS) --serve $(PORT) --variant $(VARIANT) \
--record trials/$(TRIAL_NAME).yaml \
--trial trials/$(TRIAL_NAME).md $(ARGS)
diff --git a/crates/cb-render-html/src/doc.rs b/crates/cb-render-html/src/doc.rs
index e04d4c1..da3e62a 100644
--- a/crates/cb-render-html/src/doc.rs
+++ b/crates/cb-render-html/src/doc.rs
@@ -1044,11 +1044,17 @@ pub fn document_with_log(
"
GROUND \u{2014} round {round}, step {step:?}
\
lead {lead} \
scoring {mode:?} \
+ rules {variant} \
viewing as {who}
",
round = view.round,
step = view.step,
lead = seat_name(view.lead),
mode = view.mode,
+ // CB-WP-0044: the page must say which rules it is playing. It did
+ // not, so a player who ran the default and was told the layout had
+ // changed reported "no changes" — correctly, because the baseline
+ // is deliberately unchanged and nothing on screen said so.
+ variant = esc(view.variant.id()),
who = match view.viewer {
Some(p) => format!("{} (their hand only)", seat_name(p)),
None => "a spectator (no hands)".to_string(),
diff --git a/crates/cb-render-html/src/lib.rs b/crates/cb-render-html/src/lib.rs
index efacefd..0172281 100644
--- a/crates/cb-render-html/src/lib.rs
+++ b/crates/cb-render-html/src/lib.rs
@@ -187,6 +187,7 @@ mod coverage {
// that could only be satisfied by geometry would have to be
// OMITTED, and the page would be illegible to `text_of` and to a
// screen reader alike.
+ ("variant", "ground-darvo-r0"),
("problem_markers.*.owner", "P1\u{2019}s alone"),
("problem_markers.*.scope", "P2\u{2019}s Bond network"),
("outcome.total", "total 9"),
diff --git a/crates/cb-render-html/src/testfix.rs b/crates/cb-render-html/src/testfix.rs
index 1ebb887..4610eab 100644
--- a/crates/cb-render-html/src/testfix.rs
+++ b/crates/cb-render-html/src/testfix.rs
@@ -82,6 +82,7 @@ pub fn view(viewer: Option) -> GroundView {
(Pair::new(p2, p3), Relation::Bond),
]),
problems,
+ variant: games_ground::Variant::Baseline,
focus: BTreeMap::from([(p1, p3)]),
selections: BTreeMap::from([
(
diff --git a/games/ground/src/view.rs b/games/ground/src/view.rs
index 02e6832..e5e8ac5 100644
--- a/games/ground/src/view.rs
+++ b/games/ground/src/view.rs
@@ -41,6 +41,13 @@ pub struct GroundView {
pub lead: PlayerId,
pub step: RoundStep,
pub mode: ScoringMode,
+ /// Which rules package is being played (CB-WP-0044).
+ ///
+ /// **The page had no way to say this**, so a player could not tell
+ /// baseline from H1 from H2 by looking — and reported a variant
+ /// change as "no changes" after playing the default.
+ #[serde(default)]
+ pub variant: crate::Variant,
pub players: BTreeMap,
pub relations: BTreeMap,
pub problems: BTreeMap,
@@ -170,6 +177,7 @@ impl Project for GroundState {
lead: self.lead,
step: self.step,
mode: self.mode,
+ variant: self.variant,
players: self
.players
.iter()
diff --git a/tools/cb-play/src/inspect.rs b/tools/cb-play/src/inspect.rs
index 1ede001..827d465 100644
--- a/tools/cb-play/src/inspect.rs
+++ b/tools/cb-play/src/inspect.rs
@@ -140,11 +140,12 @@ fn render_player(id: PlayerId, p: &PlayerView, is_viewer: bool) -> String {
pub fn render(view: &GroundView) -> String {
let mut out = String::new();
out.push_str(&format!(
- "\nround {} step {:?} lead {} mode {:?} deck {} discard [{}]\n",
+ "\nround {} step {:?} lead {} mode {:?} rules {} deck {} discard [{}]\n",
view.round,
view.step,
seat_name(view.lead),
view.mode,
+ view.variant.id(),
view.solution_deck_len,
cards(&view.solution_discard),
));
@@ -493,6 +494,9 @@ mod tests {
("step", "step Resolve"),
("lead", "lead P2"),
("mode", "mode BondedCoalitions"),
+ // CB-WP-0044: the inspector must say which rules it is replaying,
+ // for the same reason the page must.
+ ("variant", "rules ground-darvo-r0"),
("viewer", "(you)"),
("solution_deck_len", "deck 11"),
("solution_discard.*.suit", "discard [Repair, Change]"),
@@ -617,6 +621,7 @@ mod tests {
]),
problems,
problem_markers: Default::default(),
+ variant: games_ground::Variant::Baseline,
focus: BTreeMap::from([(p1, p3)]),
selections: BTreeMap::from([
(
diff --git a/trials/2026-08-08-1734.md b/trials/2026-08-08-1734.md
new file mode 100644
index 0000000..056e522
--- /dev/null
+++ b/trials/2026-08-08-1734.md
@@ -0,0 +1,18 @@
+# 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 | 0 | 1 | Select | 39c2ed586d57 | Ok, all problems are on the table, so they are all global? |
+| 2 | 1 | 32 | 5 | after the end | c852f3b15ee8 | Well, i did not notice any difference, maybe i did not start the H2 variation? |
+
+
diff --git a/trials/2026-08-08-1734.yaml b/trials/2026-08-08-1734.yaml
new file mode 100644
index 0000000..1e27ee1
--- /dev/null
+++ b/trials/2026-08-08-1734.yaml
@@ -0,0 +1,149 @@
+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: 3
+- 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: SOLVE
+ problem: 2
+- actor: P2
+ cmd: select_action
+ args:
+ action: INVESTIGATE
+ problem: 4
+- 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: SOLVE
+ problem: 3
+- actor: P2
+ cmd: select_action
+ args:
+ action: SOLVE
+ problem: 3
+- actor: P3
+ cmd: select_action
+ args:
+ action: SOLVE
+ 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: P2
+- actor: P2
+ cmd: select_action
+ args:
+ action: SUPPORT
+ target: P1
+- actor: P3
+ cmd: select_action
+ args:
+ action: SUPPORT
+ target: P1
+- actor: SYSTEM
+ cmd: reveal
+ args: {}
+- actor: P1
+ cmd: respond_to_support
+ args:
+ response: accept_bond
+- actor: P2
+ 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: c852f3b15ee8a8e68f0cecfa3c4a7dd7f1b3413a1b90df496e9418d121e6d9a4
diff --git a/workplans/CB-WP-0044-the-page-says-which-rules-it-plays.md b/workplans/CB-WP-0044-the-page-says-which-rules-it-plays.md
new file mode 100644
index 0000000..976b445
--- /dev/null
+++ b/workplans/CB-WP-0044-the-page-says-which-rules-it-plays.md
@@ -0,0 +1,75 @@
+---
+id: CB-WP-0044
+kind: product
+title: "The page says which rules it plays"
+status: done
+---
+
+# Purpose
+
+```
+structural tier M (adds a field to the projection, a canonical
+ interface, and a driver knob)
+declared tier M
+```
+
+## The report
+
+> *"I did a session and noted no changes."*
+
+**Correct, and the fault was ours twice over.**
+
+`make ground` passes no `--variant`, so the session was **baseline** — and
+CB-WP-0043 deliberately leaves the baseline layout untouched, because a
+variant that redraws the baseline invalidates every prior look at it.
+
+**But the deeper defect is that nothing said so.** The page named the
+round, the step, the Lead, the scoring mode and the viewer — **and never
+which rules it was playing.** `GroundView` did not even carry the variant.
+There was no way, from the screen, to tell baseline from H1 from H2.
+
+> **A session that cannot say which rules it is playing cannot report a
+> rules change.** The player did the right thing and the instrument had
+> nothing to tell them.
+
+## Task: say it, and make it reachable
+
+```task
+id: CB-WP-0044-T01
+status: done
+priority: high
+```
+
+**Controls:**
+- **the page names the variant**, beside the scoring mode where a player
+ already looks for what game this is;
+- **the inspector names it too** — a replay that cannot say which rules
+ produced it is the same defect in the other tool;
+- **`make ground` can select it**, or the capability exists and nobody
+ can reach it;
+- **verified end to end against a served page**, not by reading the code.
+
+**Done 2026-08-08.**
+
+`variant` on `GroundView`, `rules ` in the header, `rules ` in the
+inspector, and `make ground VARIANT=h2`.
+
+**Both coverage probes caught the new field independently** — the render
+crate's and `cb-play`'s — which is the second time in two passes that the
+probes have turned an addition into a legibility requirement rather than
+letting it be silent state.
+
+**Verified by fetching the served page**: `make ground VARIANT=h2` prints
+`rules: h2`, and the page carries `rules h2-scoped-problem-stress` plus
+the scope labels. The baseline page says `rules ground-darvo-r0` and keeps
+its row, which is the behaviour CB-WP-0043 intended and could not
+previously be told apart from a bug.
+
+## What this does not fix
+
+- **Nothing on the page explains what a scope does.** It says *whose* a
+ Problem is; it does not say that an unclaimed personal card ticks only
+ its owner at Round End. H2's `Rules_Text.csv` is vendored and unread.
+- **The variant is not in the trial log's header**, so a note written
+ during an H2 session does not record which rules it was about. That is
+ the same class of defect as this one, one artifact along.