CB-WP-0028 T04-T07: a game you solve, rankings that cite their source, and

a feature deliberately not built

T04 BUILT NOTHING, WHICH IS THE CORRECT OUTCOME. "Click the draw stack to
get hand cards" is not a legal move: GroundCommand has no standalone draw,
and the edition's own INVESTIGATE text settles it -- "Choose one hidden,
non-Denied Problem and reveal it. THEN DRAW ONE SOLUTION." Drawing is a
consequence, never an action. Implementing click-to-draw would have
invented a rule, which is what CB-WP-0023 exists to stop.

And observation 5 is already true: drawing is ALREADY automatic, inside
INVESTIGATE, with no player input. An auto-draw option was asked for a
thing that has never been manual.

Both observations have one root cause and T02 fixed it. The maintainer
expected to take cards from the deck because NOTHING ON THE PAGE SAID HOW
DRAWING WORKS -- the INVESTIGATE card's own text was in a file we had not
imported. No finding raised: a player's instinct differing from a legible
rule is a comprehension gap, not a rules gap. Whether the instinct recurs
now that the text is present is a testable question and was not before.

T06: "game solved" on a win, "game over" on a loss, "the game stopped"
with no outcome. Asserted all three ways, because a test checking only the
win case passes for a page that always says solved. More than tone --
GROUND is co-operative and about repairing something, and arcade
failure-state vocabulary for a win tells a player the wrong thing about
what they just did.

T05: the ending page gets the table's two-column shape. Result left;
rankings, controls and the full log right. The seal still removes every
control wherever they now live.

T07's interesting decision was NOT TO RANK. Modes.csv defines
scoring_tiebreak per mode, so ordering is the GAME's where one exists --
"Lower combined Stress, then fewer Blame tokens" for coalitions. For
SHARED GROUND it says "Not applicable", because the table succeeds or
fails together. So co-op shows contributions and refuses to order them,
and says why. Drawing a leaderboard because a leaderboard is easy would
invent scoring the rules do not have. The one derived superlative is
labelled "clay-borg's reading, not a rule" and appears only where the mode
ranks; ties are shown as ties.

52 render tests pass. check clean, loop-lint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-06 17:25:48 +02:00
parent 4b5b601ce0
commit 4d3e30eda4
3 changed files with 275 additions and 9 deletions

View file

@ -1138,6 +1138,89 @@ mod ending_page {
);
}
/// **CB-WP-0028 T06, asserted both ways.** A test that only checked
/// the win case would pass for a page that always says "solved".
#[test]
fn a_won_game_is_solved_and_a_lost_one_is_over() {
let mut won = crate::testfix::view(None);
if let Some(o) = won.outcome.as_mut() {
o.group_success = true;
}
let mut lost = crate::testfix::view(None);
if let Some(o) = lost.outcome.as_mut() {
o.group_success = false;
}
let head = |v: &games_ground::view::GroundView| {
crate::text_of(&doc::ending(Some(v), "m", "/command?t=x", &[], &[]))
};
assert!(
head(&won).contains("game solved"),
"a won game said otherwise"
);
assert!(
!head(&won).contains("game over"),
"\"game over\" is arcade vocabulary for a failure state"
);
assert!(
head(&lost).contains("game over"),
"a lost game said otherwise"
);
assert!(!head(&lost).contains("game solved"));
// And a game with no outcome claims neither.
let none = crate::text_of(&doc::ending(
None,
"P1 ran out of input",
"/command?t=x",
&[],
&[],
));
assert!(!none.contains("game solved") && !none.contains("game over"));
}
/// **CB-WP-0028 T07.** Every ranking names its source, and the
/// co-operative mode is not ranked at all — the game says its
/// tiebreak is "Not applicable", and ranking it anyway would invent
/// scoring the rules do not have.
#[test]
fn a_cooperative_game_shows_contributions_and_refuses_to_rank_them() {
let mut v = crate::testfix::view(None);
v.mode = games_ground::ScoringMode::SharedGround;
let text = crate::text_of(&doc::ending(Some(&v), "m", "/command?t=x", &[], &[]));
assert!(
text.contains("problems solved"),
"the countable fact is missing"
);
assert!(
text.contains("not ranked"),
"SHARED GROUND must not be ranked: {text}"
);
assert!(
!text.contains("clay-borg's reading"),
"a reading must not be offered where the game defines no ranking"
);
}
/// And the inverse, or the test above passes for a page that never
/// ranks anything. A ranked mode cites the GAME's tiebreak and marks
/// anything derived as ours.
#[test]
fn a_ranked_mode_cites_the_games_own_tiebreak() {
let mut v = crate::testfix::view(None);
v.mode = games_ground::ScoringMode::BondedCoalitions;
let text = crate::text_of(&doc::ending(Some(&v), "m", "/command?t=x", &[], &[]));
assert!(
text.contains("Lower combined Stress"),
"the tiebreak shown must be the edition's own words: {text}"
);
assert!(
text.contains("clay-borg&#39;s reading") || text.contains("clay-borg's reading"),
"a derived superlative must be marked as ours, not as a rule"
);
}
/// The negative control. If `seal` fired on any reply, this test would
/// pass for a page that tears itself down whenever it is touched —
/// which would break `play again` in the ordinary case.