fix: the controls sit below the log, and ending a session greys the page
Some checks failed
ci / check (push) Failing after 4s

Tier S (fixes inside a boundary; chaos d8=3, no override). Two
observations from play, both about what the page says has happened.

The controls moved BELOW the log: you read what happened, then decide what
to do next. They were above it, which asks for the decision first.

And sealing now marks the WHOLE PAGE inert, not just the controls. A
greyed-out button beside a full-colour table still reads as a live game
with one broken control; the session has ended and everything on screen is
a record of it. The status line stays legible on purpose -- it is the one
thing still worth reading.

THE STUB NEEDED A BODY classList TO MAKE THAT TESTABLE. Without it "the
session visibly ended" would have been a claim about CSS with nothing
checking it, which is precisely CB-WP-0016's finding: a stub too thin to
express a failure is how the failure survives. The harness reports the
sealed page through the status channel with a NUL-separated marker --
ugly, deliberate, and documented, because widening the return type would
touch every caller for one boolean.

Both directions asserted: a `closed` reply seals the page, an `ok:
dealing` reply does NOT -- otherwise the seal test would pass for a page
that greys itself whenever it is touched, breaking `play again`.

63 render tests, 26 cb-play, check and loop-lint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-06 22:38:32 +02:00
parent b12725566b
commit 51efe55d14
5 changed files with 224 additions and 3 deletions

View file

@ -1326,6 +1326,28 @@ mod ending_page {
status.contains("session has ended"),
"the page must say what happened: {status:?}"
);
// The WHOLE page goes inert, not only the controls. A greyed
// button beside a full-colour table reads as a live game with one
// broken control.
assert!(
status.contains("sealed-page"),
"the page itself was not marked ended: {status:?}"
);
}
/// The controls sit **below** the log: you read what happened, then
/// decide what to do next.
#[test]
fn the_controls_come_after_the_log() {
let html = page();
let log = html.find("<h2>log</h2>").expect("a log section");
let again = html
.find("data-drop=\"again\"")
.expect("a play-again control");
assert!(
again > log,
"the controls are above the log; the reader decides before reading"
);
}
/// **A thing you click must not look like a thing you drag.**
@ -1470,6 +1492,12 @@ mod ending_page {
live.contains(&"again".to_string()) && live.contains(&"done".to_string()),
"an 'ok' reply must not seal the page: {live:?}"
);
let (_, status) =
jsrun::gesture_with_reply(&html, "again", "again", "ok: dealing").expect("run");
assert!(
!status.contains("sealed-page"),
"a dealing reply greyed out a page that is about to be reused"
);
}
}