From fd76db4f713a9a1d7b2fd949f3ef28cfe28fb8d4 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 8 Aug 2026 18:38:09 +0200 Subject: [PATCH] CB-WP-0045: the table names what it shows Three reports from the 2026-08-08 sessions. H2 lands ("that seemed more interesting"), two legibility gaps do not. The number was only a FALLBACK for a missing title, so it showed exactly when it was least useful. That was survivable while Problems sat in one ordered row -- position WAS the number. CB-WP-0043 scattered them by scope and took the implicit index with it, with nothing going red because no test named the number. DARVO.csv's mandatory_effect and Actions.csv's GROUND rules_text were both vendored, both used only as tripwires, and neither ever reached the player -- F18's shape again. The modes now explain themselves at the point of choice, and NOT when no mode is on offer. Tests assert the edition's own words verbatim (ADR-0015), the number over every key in view.problems, and both halves of the mode explanation. Co-Authored-By: Claude Opus 5 --- crates/cb-render-html/src/doc.rs | 75 +++++++ crates/cb-render-html/src/lib.rs | 108 ++++++++++ trials/2026-08-08-1818-2.yaml | 158 +++++++++++++++ trials/2026-08-08-1818-3.yaml | 187 ++++++++++++++++++ trials/2026-08-08-1818.md | 19 ++ trials/2026-08-08-1818.yaml | 160 +++++++++++++++ ...B-WP-0045-the-table-names-what-it-shows.md | 102 ++++++++++ 7 files changed, 809 insertions(+) create mode 100644 trials/2026-08-08-1818-2.yaml create mode 100644 trials/2026-08-08-1818-3.yaml create mode 100644 trials/2026-08-08-1818.md create mode 100644 trials/2026-08-08-1818.yaml create mode 100644 workplans/CB-WP-0045-the-table-names-what-it-shows.md diff --git a/crates/cb-render-html/src/doc.rs b/crates/cb-render-html/src/doc.rs index da3e62a..4610b66 100644 --- a/crates/cb-render-html/src/doc.rs +++ b/crates/cb-render-html/src/doc.rs @@ -401,6 +401,9 @@ fn problem_svg(out: &mut String, priority: u32, p: &ProblemView, x: i32) { out, "\ + \ + \ + {priority}\ {label}\ {name}\ {sub}", @@ -410,6 +413,14 @@ fn problem_svg(out: &mut String, priority: u32, p: &ProblemView, x: i32) { // not remove the only identifier the player had. name = esc(&problem_title(priority).unwrap_or_else(|| format!("priority {priority}"))), tx = x + 10, + // CB-WP-0045: the number, on the card. + // + // Every move label says "Problem 7" and nothing on the table said + // which card that was. In the old row the 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 — visible exactly when it was least useful. + bx = x + 104, label = esc(&label), sub = esc(&sub), ); @@ -941,6 +952,21 @@ fn player_card(out: &mut String, id: PlayerId, p: &PlayerView, view: &GroundView // // The `support` line is the one the maintainer reported: it read // `support AcceptBond`, which names the answer and not the person. + // CB-WP-0045: **what this DARVO stage will do**, in the edition's own + // words. `DARVO.csv` carries `mandatory_effect` per stage; we vendored + // it for tripwires and never showed it, so the page said "DARVO + // Reverse" and left the player to guess. Reported as *"the GROUND and + // DARVO logic is far from self explanatory"*. + if p.darvo != games_ground::DarvoStage::Off { + if let Some(text) = darvo_stage_text(p.darvo) { + let _ = write!( + out, + "
what {:?} does{}
", + p.darvo, + esc(&text) + ); + } + } if let Some(m) = view.ground_modes.get(&id) { let _ = write!( out, @@ -1432,6 +1458,38 @@ fn support_words(r: &games_ground::SupportResponse, who: Option) -> St } } +/// The edition's own words for what a DARVO stage does (CB-WP-0045). +/// +/// From `DARVO.csv`'s `mandatory_effect`. **Not our paraphrase**: the game +/// says what its stages do and ADR-0015's discipline is to use that. +fn darvo_stage_text(stage: games_ground::DarvoStage) -> Option { + use games_ground::DarvoStage; + let want = match stage { + DarvoStage::Off => return None, + DarvoStage::Deny => "DENY", + DarvoStage::Attack => "ATTACK", + DarvoStage::Reverse => "REVERSE", + }; + games_ground::edition::darvo_stages() + .ok()? + .into_iter() + .find(|s| s.stage == want) + .map(|s| s.mandatory_effect) +} + +/// The GROUND card's own account of its three modes (CB-WP-0045). +/// +/// Shown where the mode is chosen. The card explains GR, OU and ND in one +/// passage; by the Reveal step the action card is no longer on screen, so +/// the explanation has to travel to the decision. +fn ground_modes_text() -> Option { + 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.