CB-WP-0020: the table you can read
Some checks failed
ci / check (push) Failing after 3s

Six of seven perceptual defects fixed; item 1 already passed.

T01, at the maintainer's instruction: a legal target restyles its
EXISTING border rather than drawing a new box. outline + outline-offset
drew a second rectangle, which an SVG viewport clips (the missing top and
left edges) and which made a seat's highlight card-sized. A border
already in the layout cannot move the layout.

T02: the ghost was a textContent copy of the card, which is why the line
break collapsed and it read as a second card, and why showing the
explanation destroyed the label. It is now a pill, the explanation is
appended beside the label, and the left-behind element is dimmed and
dashed. The stub grew innerHTML so a test can assert BOTH are present --
it could previously only see that something was displayed.

T03: NOT reproduced and recorded as not reproduced. The likeliest cause
is which element the browser reports -- for touch and pen the pointer is
captured to the pointerdown target, making every drop look like a
drop-on-itself, which is the other half of the report. elementFromPoint
is correct under both explanations. Separately the refusal was written in
element ids on the one surface a player reads when something goes wrong;
it now speaks the game's words and a test forbids id leakage.

T04: seat selections rendered as Debug. The coverage gate then failed my
first fix for dropping a field when target and problem were both set --
the aggregate does not produce that shape and the gate was right not to
care.

T05: the headline reads from group_success. 'Play again' is real, and its
first version was useless: run_game bound a fresh listener per game, so a
second game moved to a new port and left the tab pointing at a dead one.
One listener per session now, and the test asserts the second game is a
DIFFERENT deal.

Chaos d8=8 fired the first override at the new rate and drew S, changing
nothing -- one half of window 2's retirement condition.

CB-WP-0019 settled at $38.54/117 against $34.80/107. Eight for eight,
and the first under 20%.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 20:20:38 +02:00
parent 5e06a7d01e
commit bf24affa84
8 changed files with 584 additions and 55 deletions

View file

@ -2,7 +2,7 @@
id: CB-WP-0020
kind: product
title: "The table you can read: the card in your hand, the border that moves, the game that ends"
status: ready
status: done
---
# Purpose
@ -47,7 +47,7 @@ Quoted, because the wording carries the diagnosis:
```task
id: CB-WP-0020-T01
status: todo
status: done
priority: high
```
@ -69,11 +69,16 @@ when only the border needed to change.
reading them must stay red. This is a style change and must not become a
behaviour change.
**Done 2026-08-03.** `.dropok` restyles `border-style`/`border-color` in
place, and SVG targets change their existing `stroke` with a dash array.
No `outline` remains. **A border already in the layout cannot move the
layout**, which is what the instruction was for.
## Task: the thing in your hand looks like a thing in your hand
```task
id: CB-WP-0020-T02
status: todo
status: done
priority: high
```
@ -95,11 +100,20 @@ that pair (ADR-0010 D1) — the neighbouring-pair mutation stays red. And a
new one: the ghost must still name what is held when an explanation is
showing, so a mutation that drops the label goes red.
**Done 2026-08-03.** The ghost is a pill, not a card — it cannot be
mistaken for a second card — and the explanation is appended beside the
label rather than replacing it. The left-behind element is dimmed to 0.35
and dashed, so it reads as *left behind*.
The stub grew `innerHTML` so the test can assert **both** are present at
once; it previously could only see that *something* was displayed, which
is why the replacement went unnoticed.
## Task: say why a drop was refused, in words
```task
id: CB-WP-0020-T03
status: todo
status: done
priority: high
```
@ -120,11 +134,26 @@ every part of the page being a card, in which case the message is correct
and unreachable, and the fix is elsewhere. **Do not "fix" a message that
already works; reproduce the report first.**
**Done 2026-08-03. Not reproduced, and recorded as not reproduced.** The
harness test passes and the page has real gaps; without a browser I cannot
see it fail.
The likeliest cause is not the message but **which element the browser
reports**: for touch and pen the pointer is implicitly captured to the
`pointerdown` target, so `e.target` on `pointerup` is where you *started*.
That would make every drop look like a drop-on-itself — which is the other
half of the report. `document.elementFromPoint` is now used instead, and
it is **correct under both explanations**.
**The refusal is in the game's words**, not element ids: *"Attack on P2 is
not a move you can make right now"*. A test asserts no `action-` id leaks
into it.
## Task: show what the other players did
```task
id: CB-WP-0020-T04
status: todo
status: done
priority: medium
```
@ -141,11 +170,19 @@ do not re-phrase: a second vocabulary drifts from the first.
**Control:** a seat's rendered selection must match the log's phrasing for
the same command, asserted rather than eyeballed.
**Done 2026-08-03.** `selection_words` renders every field that is set.
**The coverage gate failed my first version**, which matched on
`(target, problem)` and showed only the target when both were present:
*"claimed rendered, but absent: selections.\*.problem"*. The aggregate does
not currently produce both — the gate does not care whether the case is
reachable, and it was right not to.
## Task: the game ends in a way you can act on
```task
id: CB-WP-0020-T05
status: todo
status: done
priority: medium
```
@ -163,11 +200,25 @@ Three things, in the order they matter:
the edition data authoritative. **Do not fix scoring here.** Confirm the
cause and record it; the import is its own pass.
**Done 2026-08-03.** The headline reads from the outcome: *"the group held
the frame"* / *"did not hold the frame"*, and only says *"game over"* when
there is no outcome to report.
**"Play again" is real, and its first version was useless.** `run_game`
bound a fresh listener per game, so a second game moved to a new port and
left the player's tab pointing at a dead one — the CB-WP-0018 defect
returning by another route. The listener is bound once per **session**
now. `play_again_deals_a_second_game` asserts the second game is a
**different deal**, not merely that one happened.
**The 0 scores and `winners nobody` are the stand-in dataset**, confirmed,
not a scoring bug. Not fixed here.
## Task: evidence
```task
id: CB-WP-0020-T06
status: todo
status: done
priority: high
```
@ -183,3 +234,21 @@ priority: high
is one half of window 2's retirement condition; record it as such.
- **Quote CB-WP-0019's cost by re-running the instrument**, per the rule
it wrote.
**Done 2026-08-03.**
[CB-EV-0018](../evidence/CB-EV-0018-the-table-you-can-read.md). `make all`
exits 0.
- **Four human checks, four sets of defects the suite called green.**
Of these seven, **two** could have been caught mechanically and neither
by a check that exists; the other five are about layout and hit-testing
and cannot be reached from here. Saying otherwise would be the error
this project keeps naming.
- **Two defects found by this project's own tests** while fixing the
reported ones: a renderer dropping a field (coverage gate) and "play
again" moving the game to a dead port.
- **Chaos: the first override at d8 changed nothing** — one half of
window 2's retirement condition.
- **CB-WP-0019 settled at $38.54/117** against $34.80/107 last reported.
**Eight for eight**, and the first under 20% — which is what re-running
at quote time is for.