Some checks failed
ci / check (push) Failing after 4s
Five remarks from the maintainer's test games, checked against the code before being written down — two had already reached ground-game on wrong premises, so a claim now names the line that makes it true. Three of the five turned out to be data the projection already carries, drawn as text: solution_deck_len, solution_discard, and OutcomeView's personal/mastery/winners. One control (`close — I have read this`) is labelled as a reading but shuts the server down, and leaves a live-looking page pointing at a dead port. One number does not exist at all: table.rs loops run_game and keeps only the last summary. CB-WP-0024 (S, chaos d8=6, declaration 7 of window 2) — the piles and the other seats' plays as objects on the table, the ending control saying what it does, and a tally that survives "play again". CB-WP-0025 (L, chaos d8=6, declaration 8) — "could we have won" and "how hard is this" are the same search asked twice. Tier L because the information boundary is the whole design problem: a solver reading GroundState sees the deck the rules hide, and would tell the maintainer he could have won by playing a card he had no way to know was there. Also unblocks GROUND-WP-0005, active with both tasks waiting on a measured difficulty baseline. Also: CB-WP-0022 T07's evidence file moves to CB-EV-0021 — CB-WP-0023 shipped CB-EV-0020 first. loop-lint: no findings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
8.3 KiB
Markdown
182 lines
8.3 KiB
Markdown
---
|
||
id: CB-WP-0024
|
||
kind: product
|
||
title: "The table you can watch: the piles, the other seats' moves, the score that carries"
|
||
status: ready
|
||
---
|
||
|
||
# Purpose
|
||
|
||
```
|
||
structural tier S (renders state the projection already carries, inside
|
||
cb-render-html and cb-play — no new port, no canonical
|
||
interface, no dependency)
|
||
chaos d8 = 6 → no override
|
||
declared tier S
|
||
```
|
||
|
||
Declaration 7 of chaos window 2.
|
||
|
||
## Five remarks from play, and what is actually true of each
|
||
|
||
The maintainer played several games and reported five things. Four are
|
||
this pass. Each was checked against the code before being written down,
|
||
because **two findings have already reached `ground-game` on wrong
|
||
premises** (CB-WP-0023 T01, GROUND-WP-0004 T02) and the rule now is that a
|
||
claim names the line that makes it true.
|
||
|
||
| # | reported | what the code says |
|
||
|---|---|---|
|
||
| 1 | *"the button next to new game says 'I need to read this' — why? the UI is not closing"* | `doc.rs:825` renders `close — I have read this`. `hotseat.rs:248` reads it as **shut the server down**. The label describes a reading; the control ends a process. And nothing closes: a page cannot close a tab it did not open, so the tab sits there showing a dead game against a dead port. |
|
||
| 4 | *"following the other players is only possible in the log"* | Wrong by degree, not in kind. `player_card` (`doc.rs:385`) draws each seat, and `selections` are `Shown` for every seat once `step != Select` (`view.rs`) — so the data is there and is being drawn as words. What is missing is that a seat's *play* is not drawn **as a card on the table**. |
|
||
| 4b | *"visualise the draw stack and the discard stack"* | Both already project: `solution_deck_len` and `solution_discard` (`view.rs`). `body()` prints them as one line of text (`doc.rs:575`). Nothing is missing from the model; the whole gap is rendering. |
|
||
| 5 | *"if we calculate scores, show them — and a cumulative score over several games"* | Per-game scores exist and **are already drawn**: `OutcomeView.personal`, `.mastery`, `.winners`, `.total`/`.threshold` (`doc.rs:582–628`). Cumulative does not exist at all: `table.rs:241` loops `run_game` and returns **only the last summary**. |
|
||
|
||
Remarks 2 (*how could we have won*) and 3 (*difficulty*) are **not this
|
||
pass** — they need a search and a measurement, not a renderer. They are
|
||
CB-WP-0025.
|
||
|
||
**So this pass is smaller than the remarks make it sound.** One control is
|
||
mislabelled, one number is missing, and three things that already project
|
||
are being written as text where a picture would read better. That is why
|
||
it is S: no new state leaves the aggregate, and `view.rs` gains nothing
|
||
except where task T04 says so explicitly.
|
||
|
||
## Task: the ending control says what it does, and the page acts on it
|
||
|
||
```task
|
||
id: CB-WP-0024-T01
|
||
status: todo
|
||
priority: high
|
||
```
|
||
|
||
Two defects behind one button.
|
||
|
||
**The label lies about the mechanism.** `done` does not mean *I have read
|
||
this*; it means *stop serving*. Rename it to what it does — the session
|
||
ends, the process exits — and drop the first-person reading claim. The
|
||
`data-drop` key stays `done`: `hotseat.rs:248` matches on the body and the
|
||
tests in `hotseat.rs:524` drive it.
|
||
|
||
**The page does not change.** After the POST the server replies `closed`
|
||
and breaks its loop; the tab keeps showing a live-looking table with a
|
||
`play again` control that now hits a closed port. Replace the page body on
|
||
acknowledgement with a terminal state that says the session has ended and
|
||
this tab can be closed — and **disable `play again` at the same moment**,
|
||
because a control that can no longer work must not look like it can.
|
||
|
||
**Controls:**
|
||
- a test asserts the acknowledged page no longer offers `again` — not just
|
||
that it shows different words;
|
||
- `hotseat.rs`'s existing `play_again_deals_a_second_game` must still
|
||
pass unchanged, or the key was renamed and the server was not told;
|
||
- the label change must be mutation-visible: assert the rendered text, so
|
||
reverting the wording turns a test red.
|
||
|
||
## Task: the piles are objects on the table
|
||
|
||
```task
|
||
id: CB-WP-0024-T02
|
||
status: todo
|
||
priority: high
|
||
```
|
||
|
||
Draw the draw stack and the discard stack as stacks, with their counts on
|
||
top, replacing the text line at `doc.rs:575`. `problem_svg` and
|
||
`relations_svg` are the precedent for how this repo draws — follow them
|
||
rather than inventing a second idiom.
|
||
|
||
The maintainer asked for one thing the model does not yet do: **when the
|
||
draw stack runs out, the discard shows a shuffle**. Before drawing that,
|
||
establish whether it happens. Search the aggregate for the reshuffle: if
|
||
`solution_deck` is refilled from `solution_discard` anywhere, render the
|
||
moment; **if it is not, that is a finding, not a rendering task** — the
|
||
deck can be exhausted and the rules do not say what then. Raise it through
|
||
CB-WP-0022's register as *underdetermined* and report it to `ground-game`.
|
||
Do not implement a reshuffle. Inventing rules is what CB-WP-0023 exists to
|
||
stop.
|
||
|
||
**Controls:**
|
||
- the drawn counts come from `solution_deck_len` and
|
||
`solution_discard.len()`, asserted against the view, never recomputed;
|
||
- a discard of zero cards renders as an empty pile, not as a missing one —
|
||
an absent slot reads as "no discard exists in this game";
|
||
- the shuffle question is answered in the task record with the line number
|
||
that settles it, either way.
|
||
|
||
## Task: what the other seats played, on the table
|
||
|
||
```task
|
||
id: CB-WP-0024-T03
|
||
status: todo
|
||
priority: high
|
||
```
|
||
|
||
Each seat's revealed selection should be legible as a played card in that
|
||
seat's area, not only as a sentence in `selection_words` and a line in the
|
||
log. The log stays — it is the record, and CB-WP-0018 built it for a
|
||
reason. This adds the picture, it does not replace the text.
|
||
|
||
**The hiding rule is not negotiable and is not this pass's to touch.**
|
||
`SelectionView::Hidden` is what a face-down selection projects, and
|
||
`a_seat_never_sees_another_seats_face_down_selection` asserts it on the
|
||
serialized view. A rendering that draws something suggestive of the hidden
|
||
choice — a suit tint, a differently-shaped back, anything varying with the
|
||
hidden value — is a leak even though the model did not leak. Draw
|
||
face-down as one identical back.
|
||
|
||
**Controls:**
|
||
- a test renders a mid-`Select` view and asserts the HTML contains nothing
|
||
that varies with another seat's hidden selection;
|
||
- the same view after Reveal shows it — otherwise the assertion above
|
||
passes for a renderer that draws nothing at all (this is the shape
|
||
`view.rs`'s own test already uses; copy it);
|
||
- `make sim` still passes: 26 scenarios encode what the table looks like.
|
||
|
||
## Task: the score that carries across games
|
||
|
||
```task
|
||
id: CB-WP-0024-T04
|
||
status: todo
|
||
priority: medium
|
||
```
|
||
|
||
`play` (`table.rs:219`) already owns the session — one listener, a seed
|
||
that advances, `run_game` in a loop — and throws away every summary but
|
||
the last. Accumulate them and show the running tally on the ending page,
|
||
beside the per-game outcome that is already drawn.
|
||
|
||
**Decide what "cumulative" means before summing anything, and write the
|
||
answer down.** `OutcomeView` carries `personal` (per seat, `i32`),
|
||
`mastery`, `group_success` and `winners`. Summing `personal` across games
|
||
and counting `winners` across games are different games' worth of
|
||
different answers, and the game is co-operative in a way that may make
|
||
neither the right one. If the rules do not say, **it is underdetermined**:
|
||
pick the one the code can defend, say in the UI which one is shown, and
|
||
raise the question rather than silently canonising a choice.
|
||
|
||
**Controls:**
|
||
- two games in one session produce a tally that is not either game's
|
||
score alone — the weakest possible assertion, and the one that catches a
|
||
tally reset by `play again`;
|
||
- the tally survives the seed advance and resets only on a new process;
|
||
- the per-game outcome block is unchanged, and a test says so.
|
||
|
||
## Task: evidence
|
||
|
||
```task
|
||
id: CB-WP-0024-T05
|
||
status: todo
|
||
priority: high
|
||
```
|
||
|
||
`evidence/CB-EV-0022-*.md`. Short — this is an S pass.
|
||
|
||
- **How many of the five remarks were already implemented**, and what that
|
||
says about the table's legibility versus its content. Three of five were
|
||
data already in the view being drawn as text. That is a finding about
|
||
rendering, and it is worth naming.
|
||
- **Whether the deck can be exhausted**, as T02 settled it.
|
||
- **What the cumulative-score question turned out to be**, and whether it
|
||
went to `ground-game`.
|
||
- **Quote CB-WP-0023's cost by re-running the instrument.**
|