clay-borg/workplans/CB-WP-0020-the-table-you-can-read.md
tegwick b8894e84b5
Some checks failed
ci / check (push) Failing after 3s
Sync hub state for CB-WP-0020
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:21:18 +02:00

11 KiB

id kind title status state_hub_workstream_id
CB-WP-0020 product The table you can read: the card in your hand, the border that moves, the game that ends done ac9dfd59-959b-4023-8749-88d1c20e928b

Purpose

structural tier  S   (presentation inside an existing capability — no new
                      port, no canonical interface, no new dependency)
chaos            d8 = 8  → OVERRIDE, tier drawn: S
declared tier    S (structural S, chaos 8 → S)

Declaration 3 of chaos window 2, and the first override at d8 — it fired on the second roll at the new rate. It changed nothing, which is the first half of window 2's retirement condition (retire if an override changes nothing twice running). One.

What the perceptual check found

The maintainer ran it on 2026-08-03. Item 1 passes — the resting affordance reads. Everything else has a defect, and several are defects in things the tests call green, because no test here can see a layout.

Quoted, because the wording carries the diagnosis:

  1. Pickable is legible. Nothing to do.
  2. "the card is picked up but the linebreak vanishes and the card below does not vanish. So it is a little as if we have two cards now"
  3. "the upper and left borders of the dashed lines seem to be hidden for some items. Also for players the rectangle is quite large. Maybe we should not have a new dashed line but just set the existing borderline to dashed so that the layout is more stable and less complicated"
  4. "replacing the content of the picked up card breaks the visual clue about a card being moved"
  5. "the orange status line does not change as you describe. It seems to switch only if i put back a card"
  6. "I cant follow the actions of the other players too well because they do not visualize on the board other than by result and the log"
  7. "maybe should read 'Game finished' if the players have been successful… There is no button to start a new game"

Task: the border moves, nothing else does

id: CB-WP-0020-T01
status: done
priority: high
state_hub_task_id: "cad5d2a7-2d3a-494d-b2ca-4ff765e152c2"

Adopt the maintainer's design instruction: a legal drop target changes its existing border to dashed and coloured. No second box.

The current outline: 2px dashed; outline-offset: 3px draws a new rectangle outside the element. That is why the top and left edges vanish on some items — an outline on an SVG <g> is clipped by the SVG viewport, and on a card it collides with the neighbour's margin. It is also why a seat's highlight is "quite large": it outlines a whole card when only the border needed to change.

  • HTML targets: restyle border-style/border-color in place.
  • SVG targets: change the existing stroke, not an added outline.

Control: the marked set must still equal the set Rust emitted (ADR-0010 D2 property 1) — the mutation that derives targets instead of 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

id: CB-WP-0020-T02
status: done
priority: high
state_hub_task_id: "b754eeab-76c6-4bbe-b470-c83595e1bb2a"

Two reports, one cause. The ghost is a textContent copy of the card, so (2) the <br> collapses and it reads as a second card, and (4) the explanation replaces the label, so the only thing identifying what you are carrying disappears exactly when you need it.

  • The ghost must not impersonate a card. Make it read as a held token: smaller, distinct, and it may keep the line break.
  • The explanation is additional, never a replacement. Carrying "Attack" and hovering P2 should show both — the label and "commit Attack against P2".
  • The element left behind must look left behind, so there are not two cards.

Control: the description shown must still be the one Rust wrote for 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

id: CB-WP-0020-T03
status: done
priority: high
state_hub_task_id: "5150e851-f8f2-4c18-af3f-3bc16dd301ea"

Reported: the status line "seems to switch only if i put back a card", and then reads action-attack -> action-attack is not a legal move here.

Two defects.

The refusal is written in element ids. resolve returns "{down} -> {up} is not a legal move here", which is the vocabulary of the DOM, not of the game. A player reads "you cannot Attack yourself".

And the no-target path apparently never fires. CB-WP-0018 added "took … , let go over nothing droppable" for a drop on nothing, and the maintainer did not see it. Find out why before changing anything — the likely answer is that there is almost no bare background to drop on, 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

id: CB-WP-0020-T04
status: done
priority: medium
state_hub_task_id: "7896a555-3b36-4143-a9b2-fb9184fb1a41"

Reported: "I cant follow the actions of the other players".

After Reveal, selections are public (GR-R04) and the seat card does carry them — as Selection { action: Solve, target: None, problem: Some(1) }. That is a Debug impl on a player-facing surface, which is the same defect the log fixed in CB-WP-0018 and did not fix here.

Render a seat's revealed selection in the same words the log uses. Reuse, 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

id: CB-WP-0020-T05
status: done
priority: medium
state_hub_task_id: "0a889c81-aa01-411f-bfb5-02e9558d4a05"

Three things, in the order they matter:

  • "game over" is the wrong headline when the group succeeded. It should say what happened — the outcome carries group_success, so use it.
  • A new game. Reported as "would be nice", and it is more than that: the only way to play twice today is to go back to a terminal, which makes the browser a strictly worse client than the CLI it was meant to replace.
  • The scores read 0 and the winner is nobody. That is very likely the stand-in dataset, not a scoring bug — GROUND-WP-0002 T01 has now ruled 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

id: CB-WP-0020-T06
status: done
priority: high
state_hub_task_id: "33786601-914e-4557-8a92-11c5d140118d"

evidence/CB-EV-0018-*.md.

  • What the perceptual check bought this time, and the running count: four runs, four sets of defects no test could reach.
  • Which of these the test suite called green, and whether any of them could have been caught mechanically — for those that could, say why they were not, and for those that could not, say so plainly rather than proposing a check that would not work.
  • Chaos: the first override at d8, and that it changed nothing. That 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. 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.