CB-WP-0024 T01/T02: the ending control says what it does, and the piles are objects
T01. The maintainer asked why the button says "I need to read this" and
why nothing closes. Two defects behind one control: the label described a
reading while the control STOPS THE SERVER (hotseat.rs reads `done` and
breaks its loop), and acknowledging it changed nothing on screen -- the
tab kept a live table and a `play again` pointing at a closed port.
Label is now "end session -- stops the game server". The reply says the
session has ended and the tab can be closed. The script seals the page on
a `closed` reply: removeAttribute('data-drop') on every control, so they
stop being droppable by the same rule that made them droppable. CSS is how
that reads, not the mechanism. removeAttribute rather than
setAttribute(_, null) -- the latter writes the truthy string "null" in a
browser, so the control would stay live while the stub called it sealed.
THE REPLY PATH HAD NEVER BEEN EXECUTABLE IN A TEST. jsrun's fetch stub
returned {then: function(){return this}}, which never invoked its
callbacks, so every line of the script reacting to the server was
unreachable from every test in this project. That is why the defect
survived: a page ignoring the server looked identical to one acting on it.
The stub now delivers a real then-chain and gesture_with_reply reports
which controls survive. The seal is mutation-proven -- deleting the
`closed` branch turns exactly one test red -- and a negative control
asserts an `ok: dealing` reply does NOT seal, since a seal that fired on
every reply would pass the first test and break `play again`.
T02. Draw and discard drawn as offset stacks with their counts. The
shuffle question the task required answering is settled and the answer is
that it already works: games/ground/src/lib.rs:1419-1435 implements the U4
default -- deterministic reshuffle of the discard seeded from seed ^ round,
skip the draw if both are empty -- and ground-game CONFIRMED U4 on
2026-08-03. A ruled rule, not an invented one, nothing to raise. The event
already reads out in the log; what the piles add is the state before it
fires, which is derivable from the view. A claim that a reshuffle HAS
happened would not be, and is not made.
The coverage gate caught its own probe going stale when the "17 remaining"
text was replaced. The count now lives in the pile's <title> -- a stable
probe and what a screen reader announces, where the on-canvas numeral
could be any number on the page.
39 tests pass; cb-play 22 including play_again_deals_a_second_game.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
5f9c2839bb
commit
1edb10d5a3
5 changed files with 443 additions and 15 deletions
|
|
@ -2,7 +2,7 @@
|
|||
id: CB-WP-0024
|
||||
kind: product
|
||||
title: "The table you can watch: the piles, the other seats' moves, the score that carries"
|
||||
status: ready
|
||||
status: active
|
||||
state_hub_workstream_id: "5f17b6f9-cd4b-4c31-a8af-712313149cf2"
|
||||
---
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ except where task T04 says so explicitly.
|
|||
|
||||
```task
|
||||
id: CB-WP-0024-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "acb4231c-35df-490f-93fd-be71c0abf1dc"
|
||||
```
|
||||
|
|
@ -75,11 +75,36 @@ because a control that can no longer work must not look like it can.
|
|||
- the label change must be mutation-visible: assert the rendered text, so
|
||||
reverting the wording turns a test red.
|
||||
|
||||
**Done 2026-08-05.** Label is now `end session — stops the game server`;
|
||||
the reply is `closed — the session has ended and the server has stopped.
|
||||
You can close this tab.`; and the script **seals** the page on a `closed`
|
||||
reply — `removeAttribute('data-drop')` on every control, so they stop
|
||||
being droppable by the same rule that made them droppable. `sealed` CSS is
|
||||
how that reads, not the mechanism.
|
||||
|
||||
**`removeAttribute`, not `setAttribute(_, null)`** — the latter writes the
|
||||
literal string `"null"` in a browser, which is truthy, so the control
|
||||
would stay live while the stub reported it sealed.
|
||||
|
||||
**The reply path had never been executable in a test.** `jsrun`'s `fetch`
|
||||
stub returned `{then: function(){return this}}`, which never invoked its
|
||||
callbacks — so every line of the script that reacts to what the server
|
||||
said was unreachable from every test in the project. **That is why this
|
||||
defect survived**: a page that ignores the server looked identical to one
|
||||
that acts on it. The stub now delivers a real then-chain, and
|
||||
`gesture_with_reply` reports the page's surviving controls.
|
||||
|
||||
Three tests, and the seal is mutation-proven: deleting the `closed` branch
|
||||
turns exactly one red. The negative control (`ok: dealing` must NOT seal)
|
||||
exists because a seal that fired on every reply would pass the first test
|
||||
and break `play again`. `cb-play`: 22 passed, including
|
||||
`play_again_deals_a_second_game`.
|
||||
|
||||
## Task: the piles are objects on the table
|
||||
|
||||
```task
|
||||
id: CB-WP-0024-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "37b16a87-e791-4c85-9f60-671812f6dd2e"
|
||||
```
|
||||
|
|
@ -107,6 +132,33 @@ stop.
|
|||
- the shuffle question is answered in the task record with the line number
|
||||
that settles it, either way.
|
||||
|
||||
**Done 2026-08-05.** Both piles are drawn as offset stacks with their
|
||||
counts, replacing the text line. Depth suggests *a pile exists*; the
|
||||
number is the truth — 17 cards is not seventeen rectangles.
|
||||
|
||||
**The shuffle question is settled and the answer is that it already
|
||||
works.** `games/ground/src/lib.rs:1419-1435` (`draw_solution`) implements
|
||||
the **U4 default**: deck empty → deterministic reshuffle of the discard
|
||||
via a `DeckReshuffled` event seeded from `seed ^ round`; both empty → skip
|
||||
the draw. ground-game **confirmed U4 on 2026-08-03**, so this is a ruled
|
||||
rule, not an invented one. Nothing to raise.
|
||||
|
||||
The event already reads out in the log (`hotseat.rs:374`). What the piles
|
||||
add is the *state before it fires*: deck 0 with a non-empty discard draws
|
||||
an arrow and says **"shuffles in on next draw"**. That is derivable from
|
||||
the view; a claim that a reshuffle *has happened* would not be, and is not
|
||||
made.
|
||||
|
||||
**The coverage gate caught the probe going stale** — removing the
|
||||
`17 remaining` text broke `solution_deck_len`'s classification. Fixed by
|
||||
putting the count in the pile's `<title>` (`draw pile: 17 remaining`),
|
||||
which is both a stable probe and what a screen reader announces; the
|
||||
on-canvas numeral alone could be any number on the page.
|
||||
|
||||
Three tests: counts come from the view, an empty discard renders as an
|
||||
empty pile rather than vanishing, and the reshuffle notice appears only
|
||||
when the deck is actually out.
|
||||
|
||||
## Task: what the other seats played, on the table
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue