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

@ -0,0 +1,152 @@
# CB-EV-0018 — four human checks, four sets of defects no test could reach
CB-WP-0020 T06. Measured 2026-08-03 at `5e06a7d`+. Pass kind `product`,
tier **S** (structural S; **chaos d8 = 8 → OVERRIDE, drawn S**).
Declaration 3 of chaos window 2.
---
## 1. What the check found, and what the suite said about it
Seven items. **Item 1 passed** — the resting affordance reads without
touching anything. Every other one was a defect, and `make all` was green
for all of them.
| # | reported | cause |
|---|---|---|
| 2 | *"as if we have two cards now"*, the line break vanishes | the ghost was a `textContent` copy of the card |
| 3 | *"upper and left borders … hidden"*, seat highlight *"quite large"* | `outline` + `outline-offset` drew a **second** rectangle; an SVG viewport clips it |
| 4 | *"replacing the content … breaks the visual clue"* | the explanation **replaced** the ghost's label |
| 5 | status line *"switches only if i put back a card"* | see §3 — not reproduced, and fixed anyway |
| 6 | *"cant follow the actions of the other players"* | seat cards rendered `Selection { action: Solve, … }``Debug` on a player surface |
| 7 | *"game over"* wrong when the group succeeded; no way to play again | the ending was written for one outcome and one game |
### Could any of them have been caught mechanically?
Honestly, **two of the seven**, and neither by a check that existed:
- **#6** is the same `{:?}`-on-a-player-surface defect the log fixed in
CB-WP-0018, in a place that pass did not look. A grep for `{:?}` in
emitted output would find it, and that check does not exist. **Worth
building.**
- **#7's headline** is a branch nobody wrote a case for; a test asserting
the ending page differs on `group_success` would have caught it.
**The other five cannot be caught here and saying otherwise would be the
error this project keeps naming.** #2, #3 and #4 are about what a layout
*looks like*; #5 is about which element a browser reports under a pointer.
QuickJS has no layout engine and no hit-testing, and a stub that grew one
would be asserting against my model of a browser rather than a browser.
## 2. The border moves, nothing else does
Adopted as instructed: a legal target restyles its **existing** border
rather than drawing a new box.
```
.dropok{border-style:dashed;border-color:#9cf;background:#1d2a33}
.dropok circle,.dropok rect{stroke:#9cf;stroke-dasharray:5 3}
```
That is the whole fix for the missing top and left edges — an `outline` on
an SVG `<g>` is clipped by the viewport, and on a card it collided with
the neighbour's margin. It also removes the oversized seat highlight,
because the border was always the right size. **A border already in the
layout cannot move the layout**, which is what *"more stable and less
complicated"* was asking for.
## 3. The report I could not reproduce, and what I did instead
Item 5 said the *"nothing droppable"* message never appears. The harness
test for it passes, the page has real gaps to drop into, and I have no
browser — so I could not reproduce it, and **the task said not to fix a
message that already works.**
The likeliest explanation is not the message but **which element the
browser reports**: for touch and pen a browser implicitly captures the
pointer to the `pointerdown` target, so `e.target` on `pointerup` is the
element you *started* on wherever you release. That would make every drop
look like a drop-on-itself — which is exactly the other half of the
report, *"switches only if i put back a card, then showing `action-attack
-> action-attack`"*.
So the fix is to stop asking the event and ask the document:
`document.elementFromPoint(clientX, clientY)`. **That is correct under
both explanations** — with a mouse it returns the same element `e.target`
would; with capture it returns the truth instead of the capture target.
**Recorded as unreproduced, not as diagnosed.** If item 5 still misbehaves
after this, the cause is something else and this note is the starting
point.
**And the refusal was written in element ids.** `action-attack ->
action-attack is not a legal move here` is the vocabulary of the DOM on
the one surface a player reads when something goes wrong. It now says
*"Attack needs to be dropped on something"* or *"Attack on P2 is not a
move you can make right now"*, and a test asserts no `action-` id leaks
into it.
## 4. Two defects my own tests found while fixing these
**The selection renderer dropped a field.** `selection_words` matched on
`(target, problem)` and showed only the target when both were set. The
42-path coverage gate failed the build: *"claimed rendered, but absent
from the emitted document: selections.\*.problem"*. The aggregate does not
currently produce both — but a renderer that silently drops one is the
omission class this crate exists to guard against, and the gate does not
care whether the case is reachable today.
**"Play again" moved the game to a new port.** The first version worked
end to end and would have been useless: `run_game` bound a fresh listener
per game, so dealing a second game left the player's tab pointing at a
dead port — the CB-WP-0018 defect returning by a different route. The
listener is now bound once per **session**.
`play_again_deals_a_second_game` catches it, and it asserts the second
game is a **different deal**, not merely that a second game happened — a
seed that did not advance would have satisfied a naive count.
## 5. Chaos: the first override at d8, and it changed nothing
`d8 = 8` fired on the **second roll** at the new rate and drew **S**,
which is what the structural derivation already said.
**That is one half of window 2's retirement condition** — *retire if an
override changes nothing twice running*. **One.** If the next override
also changes nothing, the mechanism goes.
Worth stating plainly so it is not mistaken for evidence either way: an
override firing on roll 2 of 12 at a 1-in-8 rate is unremarkable luck, and
drawing the structural tier is a 1-in-3 outcome. Neither number says
anything about whether d8 was the right rate. **The window needs its
twelve.**
## 6. Cost
| pass | kind | responses | cost | $/response |
|---|---|---|---|---|
| **CB-WP-0019** | meta | 117 | **$38.54** | 0.329 |
| CB-WP-0020 | product | *provisional — not quoted* | | |
Read by re-running `make status` at the moment of writing, per the rule
CB-WP-0019 wrote. CB-WP-0019 was last reported at $34.80/107 and has
settled at **$38.54/117** — **eight for eight**, and the first one under
20%, which is what re-running at quote time is supposed to do.
**Meta budget: back under the line** — CB-WP-0020 is product work, which
is what the 27% breach called for.
## 7. Open
- **The human check is four for four.** Every run has found something the
suite called green. Its cost is one maintainer session per pass; its
yield so far is a broken drag, a lying affordance list, an invisible
ending, and this set. **It should stay a gate on stage 1.**
- **A `{:?}`-in-emitted-output check** would have caught item 6 and does
not exist. Cheap; owed.
- **The engine still has not imported the edition data**, now that
GROUND-WP-0002 T01 has ruled it authoritative. That is the next product
pass, and it is what makes the ending's `0` scores and `winners nobody`
meaningful — **confirmed as the stand-in's doing, not a scoring bug**.
- **`ground-game` owes ten rulings** (U1U10) and SOLVE's legality.
- **Chaos: 3 of 12 in window 2, one override, changed nothing.**