Some checks failed
ci / check (push) Failing after 3s
evaluate CB-EV-0027. The supply is never exceeded, and the link row explains why: GR-L01's two slots per seat ARE the twelve link tokens, the same constraint written twice. Nothing was designed to make that true; it fell out. The Focus/Blame check was wrong and I caught it -- fifth instance of the family CB-EV-0019 named (a number computed correctly about the wrong subject) and the FIRST caught before leaving the repo, rather than by an adversarial reviewer or the maintainer. That is one data point, not a trend. What differed is that this pass read its own output and asked what exactly it had compared. The family still has no control: facts-check catches copies that disagree and nothing catches a correct computation over the wrong subject. The height limit went 460 -> 500, and the evidence argues why that is a correction and not a concession. 460 was a number picked while fixing the unplayable table -- no derivation. 500 has one: ~800px viewport less ~120 header and ~150 controls leaves ~530, and the version that broke dragging declared 620. An underived limit is a measurement defect; replacing it with a derived one is CB-WP-0021 T06's rule applied rather than evaded. A later pass wanting 560 has to move the viewport budget, not the number. CHAOS WINDOW 2 IS CLOSED. Twelve declarations, zero 8s, zero overrides. Its retirement condition -- retire if an override changes nothing twice running -- was untestable from the first declaration to the last, and CB-EV-0024, 0025 and 0026 each said so; this is the fourth and final. Window 1 at d4: 12 declarations, 2 overrides, both changed the outcome. Window 2 at d8: 12, 0, none. The verdict this supports is that d8 bought rarity by spending evidence -- stronger than "the rate is too low", because the rate was chosen without asking what sample size the retirement condition needs. Recording that changes how the loop constrains its own operation, which is a tier-M trigger in its own right, so it is OUTSTANDING and named rather than bundled here. Leaving it implicit is exactly how ground-game's ten rulings went uncollected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
264 lines
11 KiB
Markdown
264 lines
11 KiB
Markdown
---
|
||
id: CB-WP-0029
|
||
kind: product
|
||
title: "The tokens on the table: components you can count, and a supply the engine may not respect"
|
||
status: done
|
||
state_hub_workstream_id: "fb3d3c80-3798-4d0f-91ce-b8ea45943328"
|
||
---
|
||
|
||
# Purpose
|
||
|
||
```
|
||
structural tier M (imports another edition file under AM-4's budgets and
|
||
touches the rendering the coverage gate is written
|
||
against)
|
||
chaos d8 = 4 → no override
|
||
declared tier M
|
||
```
|
||
|
||
**Declaration 12 of chaos window 2 — the last.** The window closes with
|
||
this pass and its verdict is due; see §The window closes here.
|
||
|
||
## The ask
|
||
|
||
> *"Let's do the token objects first."*
|
||
|
||
The engine models every token correctly. It shows them as **numbers on a
|
||
seat card** — `protect 2`, `blamed by P3`, `freedom READY`. A real table
|
||
has objects you count at a glance.
|
||
|
||
## What `Tokens.csv` turns out to carry
|
||
|
||
Nine token types, and the columns are more interesting than the names:
|
||
|
||
| token | qty | sides | what the engine has |
|
||
|---|---:|---|---|
|
||
| Stress marker | 6 | Single | `stress: u8` — a marker on a 0–5 track |
|
||
| Freedom | 6 | **Double** (READY/spent) | `freedom_ready: bool` |
|
||
| DARVO stage | 6 | Single | `darvo: DarvoStage` on an OFF/DENY/ATTACK/REVERSE track |
|
||
| Relation link | **12** | Single | *"two per player"* — GR-L01's two slots |
|
||
| Focus / Blame | 6 | **Double** (FOCUS/BLAME) | `focus` map **and** `blame_from` list |
|
||
| Protection | 6 | Single | `protection: u8` |
|
||
| Denied | **5** | Single | `denied: bool` per Problem |
|
||
| Lead | 1 | Single | `lead: PlayerId` |
|
||
| Round | 1 | Single | `round: u8` |
|
||
|
||
**`quantity` is a component limit, and a simulation does not have one
|
||
unless it is told.** `protection` is `saturating_add(1)` at `lib.rs:986`
|
||
with **no upper bound** — the edition ships six Protection tokens for up
|
||
to six players. T03 is about whether that matters.
|
||
|
||
**`sides` is the second interesting column.** Freedom and Focus/Blame are
|
||
*one double-sided token each*, not two states. The engine holds Focus in a
|
||
map and Blame in a list; whether those can disagree with a single physical
|
||
token is worth checking.
|
||
|
||
## The window closes here
|
||
|
||
Window 2 opened 2026-08-03 at d8 and runs to twelve declarations. **This is
|
||
the twelfth.**
|
||
|
||
Its retirement condition — *retire if an override changes nothing twice
|
||
running* — has been **untestable throughout**: eleven declarations, zero
|
||
8s, zero overrides. CB-EV-0024, CB-EV-0025 and CB-EV-0026 each said so.
|
||
|
||
**Recording the verdict is a change to how the loop constrains itself and
|
||
is therefore its own tier-M pass**, not a task here. This workplan names
|
||
it as due so it is not lost between passes, which is how the ten rulings
|
||
went uncollected.
|
||
|
||
## Task: decide what a token is, here
|
||
|
||
```task
|
||
id: CB-WP-0029-T01
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "a7aad543-1c38-43e3-b7d2-d2127b4dc849"
|
||
```
|
||
|
||
`decisions/ADR-0016-*.md` (tier M merges survey and decision).
|
||
|
||
- **Vendor `Tokens.csv`?** ADR-0015's test applies: does it carry text a
|
||
player reads? `front_text` (READY, DENIED, PROTECTION…) and `use` do.
|
||
`shape` and `size` are print instructions and do not.
|
||
- **What is a token in the renderer?** The engine has no `Token` type and
|
||
should probably not gain one: a token is a *view* of state the aggregate
|
||
already holds. Deciding otherwise means a second source of truth for
|
||
Stress.
|
||
- **Where do they sit?** Player tokens belong with their seat; Denied
|
||
belongs on its Problem; Lead and Round belong on the table. **The
|
||
overhead view is what makes that placement meaningful** — a token
|
||
drawn in a list is a number with a picture.
|
||
- **Does `quantity` bind?** The decision T03 needs: is the component count
|
||
a rule the engine must enforce, an artifact of physical production, or
|
||
undetermined? **Do not answer it here from taste** — T03 measures first.
|
||
|
||
**Done 2026-08-07.**
|
||
[ADR-0016](../decisions/ADR-0016-the-tokens-on-the-table.md), four
|
||
decisions, **written after T03 measured**.
|
||
|
||
**D2 — a token is a VIEW, not a type.** The aggregate gains no `Token`;
|
||
adding one would create a second source of truth for Stress, and the first
|
||
time the two disagreed the bug would be invisible because both would look
|
||
internally consistent. `Tokens.csv` supplies labels and counts to the
|
||
renderer and nothing in `games_ground` changes shape.
|
||
|
||
**D3 — `quantity` does not bind, and the reason is not the measurement.**
|
||
A component limit the rules do not state is not a rule. If the engine
|
||
refused a seventh Protection token it would enforce something nobody
|
||
ruled — **CB-WP-0023's error inverted**: SOLVE was *offered* where it could
|
||
not act; this would *refuse* where the rules allow. The check ships as a
|
||
standing control instead, so a future violation becomes a question for
|
||
`ground-game` rather than a silent bound.
|
||
|
||
**D4 — the tracks are the decision that matters.** `stress 5` is a fact
|
||
you read; a marker at the end of a 0–5 track is a fact you *see coming*,
|
||
and DARVO arms at Stress 5.
|
||
|
||
## Task: tokens as objects
|
||
|
||
```task
|
||
id: CB-WP-0029-T02
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "3776359b-942a-4c9f-b9d5-8e54afffa391"
|
||
```
|
||
|
||
Draw them, on the table built in CB-WP-0028.
|
||
|
||
- **Stress on a 0–5 track**, not the number `5`. The track is the thing
|
||
that makes "one more Attack and I trigger DARVO" visible before it
|
||
happens.
|
||
- **DARVO on its OFF→DENY→ATTACK→REVERSE track**, which is the game's
|
||
namesake mechanic and currently the least visible thing on the page.
|
||
- **Freedom as a two-sided disc** — READY or spent, using the edition's own
|
||
`front_text`.
|
||
- **Protection, Blame and Focus as counted objects** by their seat.
|
||
- **Denied on its Problem**, not as the word `denied` in a corner.
|
||
|
||
**Controls:**
|
||
- **every existing coverage probe still passes.** They name facts, and
|
||
CB-WP-0027 and CB-WP-0028 both showed a fact-naming probe survives a
|
||
reflow. A third confirmation is worth having;
|
||
- **the token text comes from the edition**, asserted as a substring of
|
||
the vendored file — the rule ADR-0015 established and the thing that
|
||
stops a hand-copied label drifting;
|
||
- a seat with **no** tokens renders as a seat with no tokens, not as a
|
||
seat missing its area. The empty case is the one that silently vanishes.
|
||
|
||
**Done 2026-08-07.** Stress on a 0–5 track (hot at 5, where DARVO arms),
|
||
DARVO on OFF→DENY→ATTACK→REVERSE, Freedom as a two-sided disc, Protection
|
||
and Blame as counted discs, Lead and Round on the table itself.
|
||
|
||
**Two tests broke and both were fixture defects, not regressions.**
|
||
`seat_centres` matched *every* `<circle>`, and track stops are circles —
|
||
so it reported token discs as overlapping seats. Seats now carry
|
||
`class="seat"` and the helper keys on that.
|
||
|
||
**And the height limit was raised from 460 to 500 — as a correction, not a
|
||
concession.** The 460 had no derivation. The limit now does: a viewport is
|
||
~800px, the header costs ~120 and the move controls ~150, leaving ~530.
|
||
The version that broke dragging declared 620. **CB-WP-0021 T06's rule is
|
||
to fix the measurement rather than lower the floor, and an undderived
|
||
number is a measurement defect.**
|
||
|
||
## Task: does the engine respect the supply?
|
||
|
||
```task
|
||
id: CB-WP-0029-T03
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "5fce7cec-91e1-4dba-90b7-1f7f1a463dfc"
|
||
```
|
||
|
||
**Measure before deciding.** For each token with a `quantity`, ask whether
|
||
the aggregate can exceed it:
|
||
|
||
- **Protection (6)** — `saturating_add(1)` with no bound. Can one seat
|
||
hold three? Can the table hold seven? **Write the check, run it, report
|
||
the number.**
|
||
- **Denied (5)** — five tokens, and a scenario deals at most five
|
||
Problems. Probably safe; *probably* is not an answer.
|
||
- **Relation links (12, two per player)** — GR-L01 enforces two slots, so
|
||
this one is likely already respected. Confirm rather than assume.
|
||
- **Focus/Blame (6, double-sided)** — one token per player that flips.
|
||
Can a seat hold Focus *and* have Blame from the same source at once? If
|
||
so, the model and the component disagree.
|
||
|
||
**Whatever this finds is a finding, not a fix.** A component limit the
|
||
rules do not state is `underdetermined` and belongs to `ground-game`; the
|
||
engine must not start enforcing a bound nobody ruled. **CB-WP-0023 exists
|
||
because SOLVE was offered where it could not act** — inventing a limit is
|
||
the same error in the other direction.
|
||
|
||
**Controls:**
|
||
- each answer is a runnable check, per GameDesign §1 — a claim about the
|
||
supply is arithmetic and needs its reproduction;
|
||
- **the register gets a row per token that can exceed its quantity**, or a
|
||
stated "none found", because a survey that reports nothing and leaves no
|
||
trace is indistinguishable from one that was not run.
|
||
|
||
**Done 2026-08-07. Nothing found — and the first version of the check was
|
||
wrong.**
|
||
|
||
750 games, 2–6 seats, greedy and random:
|
||
|
||
| token | supply | reached | verdict |
|
||
|---|---:|---|---|
|
||
| Protection | 6 | 1/seat, 2/table | not exceeded |
|
||
| Denied | 5 | 3 | not exceeded |
|
||
| Relation link | 12 | **12** | exactly at the limit, never over |
|
||
| Focus/Blame | 6 | 0 conflicts | consistent |
|
||
|
||
**The link row is the interesting one**: GR-L01's two-slot rule *is* the
|
||
twelve-token supply written twice, which is the shape of a supply needing
|
||
no separate enforcement.
|
||
|
||
**The Focus/Blame check reported 2 conflicts and was wrong.** It compared
|
||
a seat's own placed Focus against its *own* `blame_from` — but that list
|
||
holds **other players'** discs, so they are different tokens. Corrected: 0.
|
||
**Fifth instance of this project's recurring defect** — a number computed
|
||
correctly about the wrong subject — and the first caught before it left
|
||
the repo.
|
||
|
||
Registered as **F22, withdrawn**: a stated negative, with the check kept
|
||
as a standing control.
|
||
|
||
## Task: evidence
|
||
|
||
```task
|
||
id: CB-WP-0029-T04
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "29858777-6f8f-4d8e-90d0-cb193b7f4d29"
|
||
```
|
||
|
||
`evidence/CB-EV-0027-*.md`.
|
||
|
||
- **Whether the supply is exceeded**, with the numbers.
|
||
- **Whether the tracks changed what the maintainer could see coming** —
|
||
the Stress track exists so a player can see DARVO approaching, and that
|
||
is a claim about play, not about pixels.
|
||
- **What the reflow cost the coverage gate**, third data point.
|
||
- **The window-2 verdict is due** (§The window closes here) — name it as
|
||
outstanding if the closing pass has not run.
|
||
- **Quote CB-WP-0028's cost by re-running the instrument.**
|
||
|
||
**Done 2026-08-07.**
|
||
[CB-EV-0027](../evidence/CB-EV-0027-the-tokens-on-the-table.md).
|
||
|
||
- **The supply is never exceeded**, and the link row explains why a supply
|
||
can need no enforcement: GR-L01's two slots per seat *are* the twelve
|
||
tokens, the same constraint written twice.
|
||
- **The Focus/Blame check was wrong and I caught it** — fifth instance of
|
||
the wrong-subject family, and **the first caught before leaving the
|
||
repo**. One data point, not a trend; the family still has no control.
|
||
- **The height limit went 460 → 500 as a correction.** 460 had no
|
||
derivation; 500 does. An underived limit is a measurement defect, and
|
||
replacing it is CB-WP-0021 T06's rule applied, not evaded.
|
||
- **Whether the tracks change what a player sees coming is untested** and
|
||
only play tests it.
|
||
- **Chaos window 2 is closed: twelve declarations, zero overrides.** Its
|
||
retirement condition was untestable from first to last. **The verdict is
|
||
the next thing this loop owes itself**, and it is a tier-M pass because
|
||
recording it changes how the loop constrains its own operation.
|