clay-borg/workplans/CB-WP-0028-the-table-you-sit-at.md
tegwick eb8aa64c56
Some checks failed
ci / check (push) Has been cancelled
CB-WP-0028 T03/T08: one overhead table, and what the nine observations
turned out to be

T03. One table_svg: seats around an elliptical table starting at the
BOTTOM -- the viewer sits nearest the reader, as at a real table --
Problems and both stacks in the middle, each seat's played card between it
and the centre, relations drawn between seats.

Two renderers were DELETED: relations_svg and piles_svg. The task said one
table not two diagrams, and leaving the old ones would have meant drawing
the same thing twice and letting them drift.

No coverage probe cost, through a restructure that merged three diagrams
and removed two functions. Second confirmation of CB-WP-0027's finding: a
probe naming a FACT survives a reflow, one naming a PRESENTATION does not.
CB-WP-0024's "17 remaining" broke on a rendering change; this far larger
reflow broke nothing.

The new control is per seat count -- no two seat circles closer than 70px
at 2 through 6 -- asserted rather than eyeballed at three, which is the
only count anyone ever looks at.

T08 (CB-EV-0026). Seven of nine observations were engine defects, one was
a design finding, one was already true and nobody could tell.

Observations 4 and 5 both dissolved and had ONE cause: nothing on the page
said how drawing works, so a player built a mental model to fill the gap
and reported the gap as two feature requests.

The import gap was worse than "one of nineteen" -- 5 of 13 columns read
from the file we DID vendor, discarded at parse time for eight days. Rule
coverage was 59/59 throughout. The gate measures whether rules are
EXERCISED; nothing measures whether a player can READ the game, and
nothing cheaply could, which is why the person playing it is the
instrument.

TWO GATES WERE WRITTEN FOR A SMALLER WORLD, and neither was wrong when
written. edition-check compared one recorded digest against Problems.csv
regardless of which file it described -- correct with one vendored file,
comparing across files with four. And a cb-play test asserted the literal
"game over" and went red when a won game said "solved", which was T06
working; it now asserts the heading against the OUTCOME and covers the
no-outcome case the original never touched.

Chaos window 2 closes with zero overrides in eleven declarations at d8.
Third and final statement of it: d8 bought rarity by spending evidence,
and a mechanism producing no data across a full window cannot be evaluated
by that window.

make all: exit 0. 57 render tests, 26 cb-play, loop-lint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 17:33:57 +02:00

399 lines
17 KiB
Markdown

---
id: CB-WP-0028
kind: product
title: "The table you sit at: the cards' own words, an overhead view, and a game you solve rather than survive"
status: done
state_hub_workstream_id: "d37c8671-54e4-447f-af64-56f482483282"
---
# Purpose
```
structural tier M (imports more of the authoritative edition, which is
an external dataset under AM-4's budgets, and touches
the rendering the coverage gate is written against)
chaos d8 = 1 → no override
declared tier M
```
Declaration 11 of chaos window 2. Tier M: survey and decision merged;
adversarial review optional.
## Nine observations from play
The maintainer played with the new meta panel and reported nine things.
**Two are findings about the game, seven are about the engine**, and the
split matters because they go to different places.
| # | observation | where it goes |
|---|---|---|
| 1 | no incentive to attack while holding positive cards | **register** — a game-design finding |
| 2 | *"I don't understand the GROUND card"* | **here** — and it is not a design gap (below) |
| 3 | overhead view: players around a table, stacks on it | T03 |
| 4 | click the draw stack to take cards | T04 |
| 5 | optional auto-draw | T04 |
| 6 | play again / stop belong in the meta column | T05 |
| 7 | so does the full log | T05 |
| 8 | *"Game over"* is wrong when you won | T06 |
| 9 | rankings — MVP, most problems solved | T07 |
## Observation 2 is a data-import gap, and it reframes the pass
*"I don't understand the GROUND card"* reads as a design problem. It is
not: `Actions.csv` carries that card's tagline (*"Regulate. Restore the
frame. Decide."*) and its full rules text, and **we vendored one file of
nineteen**. Everything else the engine knows is a hand-transcription into
`GroundRules.md`'s 59 rules — enough to *play* the game, and nothing to
*read*. The page shows `Clarify` where the card says *"Ask What Happened."*
**Found by a player saying he did not understand something.** Rule
coverage is 59/59 and has been for weeks. See F18 and
[ADR-0015](../decisions/ADR-0015-the-cards-own-words.md).
## Task: decide what else to import, and what it costs
```task
id: CB-WP-0028-T01
status: done
priority: high
state_hub_task_id: "17e6f38c-1903-4bb2-aa20-37e76c8fc4e0"
```
`decisions/ADR-0015-*.md` (tier M merges survey and decision).
**ADR-0011 is the precedent and it constrains this.** It vendored
`Problems.csv` with a checked digest and a ~50-line hand reader, and
**refused the `csv` crate on proportion** — 17,651 lines against AM-4b's
19,742 of remaining headroom, 89% of the budget to read 20 rows.
Decide:
- **which files**, and the answer is not "all of them". `Actions`,
`Solutions` and `Modes` carry text a player reads. `BOM`, `Print_Manifest`,
`Back_Designs`, `Symbols` are production artifacts for a physical print
run and have no business here.
- **whether the hand reader survives contact.** `Solutions.csv` has
`microcopy` and `rules_text` — prose fields with commas, quotes and
possibly embedded newlines. ADR-0011 named exactly this as the revisit
condition: *"nested quoting, embedded newlines, multiple dialects."*
**If the reader cannot parse them correctly, say so and re-run the
dependency argument rather than writing a fragile parser.**
- **what happens when text and rules disagree.** `GroundRules.md` was
derived by hand from these files. If a `rules_text` contradicts a
numbered rule, that is a **finding**, and it goes to the register — not
a quiet edit of either.
- **`Extensions.csv` exists** and names content deliberately outside the
core. Decide whether it is imported at all; the answer is probably no,
but *"we did not know it existed"* must not be the reason.
**Done 2026-08-06.**
[ADR-0015](../decisions/ADR-0015-the-cards-own-words.md), six decisions.
**The gap is bigger than "one file of nineteen", and the measurement is
the decision.** Of the file we *did* vendor the engine reads **5 of 13
columns**; the discarded ones are player-facing text that had been in the
repo for eight days. And `SCN_01` is hardcoded — the edition ships **four**
scenarios and the engine has never dealt three.
**ADR-0011's revisit condition is measurably absent** — zero doubled
quotes, zero embedded newlines across every candidate file — so the
dependency argument does not get re-run. Vendored `Actions`, `Solutions`,
`Modes`. Not the production artifacts, and **not `Extensions.csv`**, which
names deliberately non-core content; it is now known to exist, which was
the real risk.
## Task: the cards say what they do
```task
id: CB-WP-0028-T02
status: done
priority: high
state_hub_task_id: "aa4453d2-a84e-4afc-8534-1feb835c4d5b"
```
Every card a player can see carries its own title, tagline and rules text,
from the edition — not from a phrase we invented.
- action cards: GROUND, SOLVE, INVESTIGATE, SUPPORT, ATTACK;
- Solution cards: the title and microcopy, not just the suit;
- the mode in play, with its own description.
**Controls:**
- **a test asserts the text comes from the dataset**, not from a Rust
literal — the whole point is that the game's own words reach the player,
and a hand-copied string drifts from the source it copies;
- the drag affordance still works: the explanation is *additional*, and
CB-WP-0020 T02 already found that replacing a label with an explanation
loses the label;
- **the page stays readable.** Five action cards with full rules text is a
wall; the tagline is the default and the rules text is on demand.
**Done 2026-08-06.** One `Table` reader with four callers (a per-file
copy is how a parser acquires four subtly different bugs), `CardText` for
Actions/Solutions/Modes, `ProblemText` for the columns already vendored.
The GROUND card now explains itself; Problems show their own titles.
**The load-bearing test asserts the text is a substring of the vendored
file**, not equal to a Rust literal — a hardcoded expectation would pass
for a hand-copied string, which is the drift this ends.
**`edition` came out from behind `#[cfg(feature = "scenarios")]`**: it was
gated because its only consumer was, but the edition is the game's own
data and the runtime now reads it. **Test machinery and game content are
different things and only one is optional.** `edition-check`, written for
a single-file world, now checks every file both ways.
## Task: an overhead view of a real table
```task
id: CB-WP-0028-T03
status: done
priority: high
state_hub_task_id: "2ff677ac-80ff-4618-ad7c-05b62c8f80c8"
```
> *"Players sitting around the table, the draw and discard stacks on it.
> An overhead view of a game table would instantly help with understanding
> what is going on."*
The seats become positions around a table rather than a row of cards; the
Problems, draw and discard sit in the middle; each seat's played card
appears in front of that seat.
**This subsumes `relations_svg`**, which already places seats on a circle
for the relationship graph — so the layout exists and is drawn twice, once
as a circle and once as a row. **One table, not two diagrams.**
**Controls:**
- the coverage gate still passes: every view field appears in the parsed
document. CB-WP-0027 showed a reflow costs nothing when probes name
facts; this is a bigger reflow and the same rule applies;
- **the viewer's own seat is identifiable at a glance** — a table where
you cannot find yourself is worse than a list;
- 2 through 6 seats all lay out without overlap, asserted per seat count
rather than eyeballed at 3.
**Done 2026-08-06.** One `table_svg`: seats around an elliptical table
starting at the **bottom** (the viewer sits nearest the reader, as at a
real table), Problems and both stacks in the middle, each seat's played
card between it and the centre, relations drawn between seats.
**Two renderers were deleted**`relations_svg` and `piles_svg`. The task
said *one table, not two diagrams*, and leaving the old ones would have
meant drawing the same thing twice and letting them drift.
**No coverage probe cost**, through a restructure that merged three
diagrams and removed two functions. **Second confirmation of CB-WP-0027's
finding**: a probe naming a *fact* survives a reflow; one naming a
*presentation* does not.
The new control is per seat count: **no two seat circles closer than 70px
at 2, 3, 4, 5 or 6 seats** — asserted rather than eyeballed at three,
which is the only count anyone ever looks at. Plus: the viewer's seat is
labelled *and* visually distinct, and a spectator is not given one.
## Task: take cards from the stack, or let the table do it
```task
id: CB-WP-0028-T04
status: done
priority: medium
state_hub_task_id: "1106c60e-112b-401f-b854-eeec566ef3d1"
```
Observations 4 and 5. Clicking the draw stack to take a card is the
natural gesture; auto-draw is for when that stops being interesting.
**Both are subject to a rule the engine already has.** Drawing is not a
free action — GR-A01 draws a Solution as part of INVESTIGATE, and the deck
is not a thing a player may simply take from. **So this task must first
establish whether "click to draw" is a legal move at all**, and if it is
not, it is a **finding for `ground-game`**, not a feature: the maintainer
expected an interaction the rules do not offer, and that gap is the
signal.
**Do not implement a draw the rules do not have.** CB-WP-0023 exists
because SOLVE was offered where it could not act.
**Controls:**
- the answer to *"is clicking the deck a legal move?"* is in the task
record with the rule that settles it;
- auto-draw changes no outcome — a game played with it and without it from
the same seed produces the same end-state hash, or it is not automation
but a rules change.
**Done 2026-08-06 — and NOTHING WAS BUILT, which is the correct outcome.**
**Clicking the deck is not a legal move.** `GroundCommand` has no
standalone draw, and the edition's own INVESTIGATE text settles it:
> *"Choose one hidden, non-Denied Problem and reveal it. **Then draw one
> Solution.** If no hidden Problems remain, draw one Solution only."*
Drawing is a **consequence** of INVESTIGATE, never an action. Implementing
a click-to-draw would have invented a rule — which is what CB-WP-0023
exists to stop.
**And observation 5 is already true.** Drawing is *already* automatic: it
happens inside INVESTIGATE with no player input at all. The maintainer
asked for an auto-draw option for a thing that has never been manual.
**Both observations have the same root cause, and T02 fixed it.** He
expected to take cards from the deck because **nothing on the page said
how drawing works** — the INVESTIGATE card's own text was in a file we had
not imported. It now reads on the card.
**No finding raised.** The rules are clear and were simply not visible; a
player's instinct differing from a legible rule is a comprehension gap,
not a rules gap. Worth ground-game knowing as design feedback if the
instinct recurs with the text present — **which is now a testable
question and was not before.**
## Task: the meta column takes the controls and the log
```task
id: CB-WP-0028-T05
status: done
priority: medium
state_hub_task_id: "6c78e44e-38fc-451b-b1a0-205b1d079ca0"
```
Observations 6 and 7. `play again` and `end session` move to the meta
column, and the ending page gets the same two-column shape as the table —
it currently has none.
The full log belongs there too, which CB-WP-0027 did for the live page and
not for the ending page.
**Controls:**
- CB-WP-0024 T01's seal still works: acknowledging the end must still
remove every control, wherever they now live;
- the ending page's existing tests pass unchanged or the change is a
regression.
**Done 2026-08-06.** The ending page gets the table's two-column shape:
result on the left, rankings + controls + the full log on the right. The
seal still removes every control wherever it now lives, and the existing
ending tests passed unchanged.
## Task: a game you solve
```task
id: CB-WP-0028-T06
status: done
priority: medium
state_hub_task_id: "df0a967e-636b-4fe5-9926-4d73030a87d7"
```
> *"'Game Over' is negative and should only be used for lost games. If the
> players won, let's use 'Game solved'."*
**Right, and it is more than tone.** GROUND is a co-operative game about
repairing a situation; `group_success` means the table *solved* something.
"Game over" is arcade vocabulary for a failure state, and using it for a
win tells the player the wrong thing about what they just did.
**Controls:**
- the heading is a function of `outcome.group_success`, asserted **both
ways** — a test that only checks the win case passes for a page that
always says "solved";
- the no-outcome case (a game that ended badly) keeps its own wording and
claims neither.
**Done 2026-08-06.** `game solved` on a win, `game over` on a loss, **the
game stopped** when there is no outcome — asserted all three ways, because
a test checking only the win case passes for a page that always says
"solved".
## Task: who did what — rankings that are not invented
```task
id: CB-WP-0028-T07
status: done
priority: medium
state_hub_task_id: "ad6e6f6f-e531-494c-b8a9-18dfdd14681c"
```
> *"A ranking of players in the final stats. 'Most valuable player', 'most
> problems solved' and other conditions that might apply."*
`OutcomeView` already carries `personal`, `winners`, `coalitions` and
`mastery`, and the aggregate knows who claimed each Problem.
**The risk is inventing scoring the game does not have.** *"Most problems
solved"* is countable from `claimed_by` and is a fact. *"Most valuable
player"* is a **judgement**, and any formula for it is a rule we made up.
So: **report what the game counts, and mark anything derived as ours.**
A superlative computed from real data is fine; a superlative presented as
if the rules defined it is the same defect as a provisional default
silently canonised.
**Controls:**
- every ranking names its source: a rule id, or *"clay-borg's reading"*;
- ties are handled and shown as ties, not broken arbitrarily — `Modes.csv`
has a `scoring_tiebreak` column, so the game may already say how;
- a ranking that no seat leads (nobody solved anything) renders as that,
not as an empty list.
**Done 2026-08-06, and the interesting decision was not to rank.**
`Modes.csv` gives `scoring_tiebreak` per mode, so the ordering is **the
game's own** where it defines one — *"Lower combined Stress, then fewer
Blame tokens"* for coalitions. **For SHARED GROUND it says "Not
applicable"**, because the table succeeds or fails together.
So in co-op the page shows **contributions and refuses to order them**,
and says why. Drawing a leaderboard because a leaderboard is easy would
have invented scoring the rules do not have — the same defect as
canonising a provisional default.
Facts the game counts (Problems claimed, personal score, Stress, Blame)
are shown plainly. The one derived superlative is labelled **"clay-borg's
reading, not a rule"** and appears only where the mode ranks at all. Ties
are shown as ties.
## Task: evidence
```task
id: CB-WP-0028-T08
status: done
priority: medium
state_hub_task_id: "a20a529e-0edf-49cc-9c80-7dba3b76cff5"
```
`evidence/CB-EV-0026-*.md`.
- **Whether importing the card text changed what the maintainer
understood** — observation 2 is the acceptance test and it has a person
attached to it.
- **Whether any `rules_text` contradicted `GroundRules.md`.** Nineteen
files were hand-transcribed into 59 rules; if the import surfaces a
disagreement, that is the most valuable output of this pass.
- **Whether "click the deck to draw" was legal**, and if not, what went to
`ground-game`.
- **What the overhead view cost the coverage gate**, against CB-WP-0027's
finding that a probe naming a fact survives a reflow.
- **Quote CB-WP-0027's cost by re-running the instrument.**
**Done 2026-08-06.**
[CB-EV-0026](../evidence/CB-EV-0026-the-table-you-sit-at.md).
- **Seven of nine observations were engine defects**, one was a design
finding, and one was already true and nobody could tell.
- **Observations 4 and 5 both dissolved**, and had one cause: nothing on
the page said how drawing works, so a player built a mental model to
fill the gap and reported the gap as two feature requests.
- **The import gap was worse than "one of nineteen"** — 5 of 13 columns
read from the file we *did* vendor, discarded at parse time for eight
days. Rule coverage was 59/59 throughout: the gate measures whether
rules are *exercised*, and **nothing measures whether a player can read
the game.**
- **Two gates were written for a smaller world** — `edition-check`
compared one digest across four files, and a `cb-play` test asserted the
literal `"game over"` and went red when a won game said *"solved"*.
Neither was wrong when written.
- **Chaos window 2 closes with zero overrides in eleven declarations.**
Third and final statement: d8 bought rarity by spending evidence, and a
mechanism producing no data across a full window cannot be evaluated by
it.