fix: a click target wearing a drag affordance made the controls look dead
Some checks failed
ci / check (push) Failing after 4s
Some checks failed
ci / check (push) Failing after 4s
Tier S (a fix inside a boundary; chaos d8=7 from the previous roll stands
for this continuation). Two observations from play that are ONE defect.
`play again`, `end session`, `pass` and the move buttons carried `.pick`,
which is cursor:grab. The stylesheet has .btn{cursor:pointer} BEFORE
.pick{cursor:grab}, so grab won.
A GRAB CURSOR INVITES A DRAG. A drag released over nothing posts nothing,
so the player picked up the button, let go, and the page did nothing. It
looked dead because the affordance told them to do the one thing that does
not work. Reported as two separate things -- "the button shows a hand to
pick up that it probably shouldn't" and "I can't start another game or
stop the server" -- and the first causes the second.
The click path itself was never broken: driving again->again and
done->done through the JS harness posts correctly. The logic was fine and
the invitation was wrong.
Click targets now carry `.tap` -- pointer cursor, same press affordance.
This extends CB-WP-0017's rule (interactive and inert must not look
identical) to: click and drag must not look identical either. The test
asserts both directions, because checking only that buttons lost `.pick`
would pass for a page with no affordances at all.
Registered F20 (applied) and F21.
F21 IS THE ONE I COULD NOT REPRODUCE: dragging did not work until after
the first note was saved. Ruled out the plausible mechanisms -- the
gesture logic posts correctly against the served page, the drag ghost
carries pointer-events:none so it cannot intercept the drop, and the
markup is identical before and after since the 303 re-renders the same
page from the same state. Remaining candidates are a <details> toggle
shifting layout mid-drag, a first-load timing difference, or browser-level
pointer capture. Reproducing it needs a browser, which no test here has --
the same gap F19 named. Recorded as unreproduced rather than given a
speculative fix.
And the fourth observation is confirmation, not a bug: "drawing my cards
from the deck is not implemented, I did not need to do that" is exactly
what CB-WP-0028 T04 determined and deliberately did not build. It is the
first evidence that importing the card text closed the comprehension gap
that produced the earlier click-the-deck request.
make all: exit 0. 62 render tests, 26 cb-play.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a6e7acd4cf
commit
b12725566b
5 changed files with 252 additions and 4 deletions
|
|
@ -220,6 +220,15 @@ h1,h2{font-size:1rem;margin:1.2rem 0 .4rem;color:#9cf}
|
|||
.pick{cursor:grab;user-select:none;box-shadow:0 2px 0 #0006,0 0 0 1px #5a7a inset;
|
||||
transition:transform .08s,box-shadow .08s}
|
||||
.pick:hover{box-shadow:0 3px 8px #000a,0 0 0 1px #7ca inset;transform:translateY(-1px)}
|
||||
/* CB-WP-0017's principle, one step further: a thing you CLICK and a thing
|
||||
you DRAG must not look identical either. `.pick` promises carry-me-
|
||||
somewhere; these promise press-me. They wore `.pick` and therefore a
|
||||
grab cursor, which invited a drag -- and a drag released over nothing
|
||||
posts nothing, so the button appeared dead. Reported both as the button
|
||||
showing a hand it should not, AND as being unable to start a new game. */
|
||||
.tap{cursor:pointer;user-select:none;box-shadow:0 2px 0 #0006,0 0 0 1px #a7d6 inset;
|
||||
transition:transform .08s,box-shadow .08s}
|
||||
.tap:hover{box-shadow:0 3px 8px #000a,0 0 0 1px #a7d inset;transform:translateY(-1px)}
|
||||
/* A legal destination for the thing currently held -- and ONLY for that
|
||||
thing. The set is written by Rust into data-targets; the script matches
|
||||
it and never derives it (ADR-0010 D1).
|
||||
|
|
@ -1062,7 +1071,7 @@ fn move_section(
|
|||
if !spatial {
|
||||
let _ = write!(
|
||||
s,
|
||||
"<div class=\"card btn pick\" data-drop=\"cmd-{i}\">{}</div>",
|
||||
"<div class=\"card btn tap\" data-drop=\"cmd-{i}\">{}</div>",
|
||||
esc(&format!("{c:?}"))
|
||||
);
|
||||
}
|
||||
|
|
@ -1075,7 +1084,7 @@ fn move_section(
|
|||
}
|
||||
if may_pass {
|
||||
s.push_str(
|
||||
"<div class=\"card btn pick\" data-drop=\"pass\">pass \u{2014} decline to act</div>",
|
||||
"<div class=\"card btn tap\" data-drop=\"pass\">pass \u{2014} decline to act</div>",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1336,8 +1345,8 @@ pub fn ending(
|
|||
let _ = write!(
|
||||
s,
|
||||
"<div class=\"row\">\
|
||||
<div class=\"card btn pick\" data-drop=\"again\">play again</div>\
|
||||
<div class=\"card btn pick\" data-drop=\"done\">end session \u{2014} stops the game server</div>\
|
||||
<div class=\"card btn tap\" data-drop=\"again\">play again</div>\
|
||||
<div class=\"card btn tap\" data-drop=\"done\">end session \u{2014} stops the game server</div>\
|
||||
</div>",
|
||||
);
|
||||
log_section(&mut s, log);
|
||||
|
|
|
|||
|
|
@ -1275,6 +1275,8 @@ mod played_cards {
|
|||
/// table and a `play again` pointing at a closed port.
|
||||
#[cfg(test)]
|
||||
mod ending_page {
|
||||
use cb_kernel::PlayerId;
|
||||
|
||||
use crate::{doc, jsrun};
|
||||
|
||||
fn page() -> String {
|
||||
|
|
@ -1326,6 +1328,53 @@ mod ending_page {
|
|||
);
|
||||
}
|
||||
|
||||
/// **A thing you click must not look like a thing you drag.**
|
||||
///
|
||||
/// Reported two ways at once: *"the button shows a hand to pick up
|
||||
/// that it probably shouldn't"* and *"I can't start another game"*.
|
||||
/// They are one defect — a grab cursor invites a drag, and a drag
|
||||
/// released over nothing posts nothing, so the button looks dead.
|
||||
#[test]
|
||||
fn click_targets_do_not_wear_the_drag_affordance() {
|
||||
let pages = [
|
||||
doc::ending(None, "m", "/command?t=x", &[], &[]),
|
||||
doc::document(
|
||||
&crate::testfix::view(Some(PlayerId(0))),
|
||||
&[games_ground::GroundCommand::SelectAction {
|
||||
action: games_ground::Action::Investigate,
|
||||
target: None,
|
||||
problem: Some(2),
|
||||
}],
|
||||
"/command?t=x",
|
||||
Some(PlayerId(0)),
|
||||
true,
|
||||
),
|
||||
];
|
||||
for html in &pages {
|
||||
for key in ["again", "done", "pass"] {
|
||||
let Some(i) = html.find(&format!("data-drop=\"{key}\"")) else {
|
||||
continue;
|
||||
};
|
||||
let tag = &html[html[..i].rfind('<').expect("an opening tag")..i];
|
||||
assert!(
|
||||
!tag.contains("pick"),
|
||||
"`{key}` is a click target wearing `.pick`, which is cursor:grab — \
|
||||
it invites a drag, and a drag onto nothing posts nothing"
|
||||
);
|
||||
assert!(
|
||||
tag.contains("tap"),
|
||||
"`{key}` must still look pressable: {tag}"
|
||||
);
|
||||
}
|
||||
}
|
||||
// The inverse, or this passes for a page with no affordances at
|
||||
// all: a real draggable still carries `.pick`.
|
||||
assert!(
|
||||
pages[1].contains("class=\"card act pick\""),
|
||||
"action cards must still be draggable"
|
||||
);
|
||||
}
|
||||
|
||||
/// **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]
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by
|
|||
| F17 | degenerate | note | — | — | 2026-08-06 | ground-game |
|
||||
| F18 | inert | raised | — | — | 2026-08-06 | clay-borg |
|
||||
| F19 | degenerate | applied | crates/cb-render-html/src/lib.rs::overhead_table | counterexample | 2026-08-06 | clay-borg |
|
||||
| F20 | inert | applied | crates/cb-render-html/src/lib.rs::ending_page | counterexample | 2026-08-06 | clay-borg |
|
||||
| F21 | degenerate | note | — | — | 2026-08-06 | clay-borg |
|
||||
|
||||
<!-- design-register:end -->
|
||||
|
||||
|
|
@ -76,6 +78,28 @@ kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by
|
|||
since GROUND-WP-0005 is blocked on exactly this number. The withdrawal
|
||||
was reported (ADR-0012 D5). Its reproduction is `difficulty.rs`, whose
|
||||
policy panel is plural *because of this finding*.
|
||||
- **F21 — dragging did not work until after the first note was saved.**
|
||||
Reported 2026-08-06: *"I could not drag and drop at the beginning but
|
||||
after I added the first comment it worked."* **`note`, and I could not
|
||||
reproduce it.** The gesture logic is correct against the served page
|
||||
(the JS harness posts properly), the ghost carries `pointer-events:none`
|
||||
so it cannot intercept the drop, and the markup is identical before and
|
||||
after the note — the 303 re-renders the same page from the same state.
|
||||
**Candidates, none confirmed:** a `<details>` toggle inside an action
|
||||
card shifting the layout mid-drag; a first-load timing difference; or a
|
||||
browser-level pointer capture. **Reproducing it needs a browser, which
|
||||
no test here has** — the same gap F19 named. Recorded rather than
|
||||
guessed at.
|
||||
- **F20 — a click target wearing a drag affordance made the ending
|
||||
controls look dead.** `play again`, `end session`, `pass` and the move
|
||||
buttons carried `.pick`, which is `cursor:grab`. A grab cursor invites a
|
||||
**drag**, and a drag released over nothing posts nothing — so the button
|
||||
did nothing and appeared broken. Reported as two separate observations
|
||||
(*"the button shows a hand to pick up that it probably shouldn't"* and
|
||||
*"I can't start another game or stop the server"*) which are one defect.
|
||||
**`applied`**: click targets now carry `.tap`. **This extends CB-WP-0017's
|
||||
rule** — interactive and inert must not look identical — to *click and
|
||||
drag must not look identical either*.
|
||||
- **F19 — the engine shipped a table nobody could play on, and every gate
|
||||
was green.** CB-WP-0028's overhead view was 620px tall, so the action
|
||||
cards sat a screen below the Problems; dragging between them was
|
||||
|
|
|
|||
17
trials/2026-08-06-2213.md
Normal file
17
trials/2026-08-06-2213.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 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.
|
||||
|
||||
<!-- trial-log:begin -->
|
||||
|
||||
| n | round | step | state_hash | comment |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 1 | Select | 47369fdf8d30 | Lets check the notes. |
|
||||
|
||||
<!-- trial-log:end -->
|
||||
149
trials/2026-08-06-2213.yaml
Normal file
149
trials/2026-08-06-2213.yaml
Normal file
|
|
@ -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: SUPPORT
|
||||
target: P2
|
||||
- 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: P2
|
||||
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: 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: SUPPORT
|
||||
target: P3
|
||||
- actor: P2
|
||||
cmd: select_action
|
||||
args:
|
||||
action: SUPPORT
|
||||
target: P1
|
||||
- actor: P3
|
||||
cmd: select_action
|
||||
args:
|
||||
action: SOLVE
|
||||
problem: 4
|
||||
- actor: SYSTEM
|
||||
cmd: reveal
|
||||
args: {}
|
||||
- actor: P3
|
||||
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: 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: SYSTEM
|
||||
cmd: resolve
|
||||
args: {}
|
||||
- actor: SYSTEM
|
||||
cmd: end_round
|
||||
args: {}
|
||||
expect:
|
||||
events: []
|
||||
state: {}
|
||||
rejects: []
|
||||
state_hash: e2ee858a1b907a4595c230747b65eede6c56574308a7ece7dec8ebe30e56fc10
|
||||
Loading…
Add table
Add a link
Reference in a new issue