clay-borg/workplans/CB-WP-0029-the-tokens-on-the-table.md
tegwick 631fb41fc2 CB-WP-0029 T01-T03: components you can count, and a supply that does not bind
ADR-0016, Tokens.csv vendored, tracks and discs on the table, and a supply
audit that found nothing and says so.

T03 MEASURED BEFORE T01 DECIDED. 750 games, 2-6 seats, greedy and random:
Protection reaches 1 per seat and 2 on the table against a supply of 6;
Denied 3 of 5; relation links EXACTLY 12 OF 12 and never more; Focus/Blame
0 conflicts. 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.

AND THE FIRST VERSION OF THE FOCUS/BLAME CHECK 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. It reported 2
conflicts; corrected, it reports 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 rather than by a reviewer.

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 they disagreed the bug would be invisible because both would look
internally consistent.

D3: quantity does NOT bind, and the reason is not the measurement. A
component limit the rules do not state is not a rule. Refusing a seventh
Protection token 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, so a
future violation becomes a question for ground-game (does the box bound
the game, or do the rules?) rather than a bound the engine invented.
Registered as F22, withdrawn: a stated negative, because a survey that
finds nothing and leaves no trace cannot be told from one never run.

D4: Stress on a 0-5 track that turns red at 5 where DARVO arms, DARVO on
OFF/DENY/ATTACK/REVERSE, Freedom as the two-sided disc the edition says it
is, Protection and Blame counted, Lead and Round on the table.

Two tests broke on token discs and both were FIXTURE defects:
seat_centres matched every <circle> and track stops are circles. Seats now
carry class="seat".

The table height limit went 460 -> 500 as a CORRECTION, not a concession.
460 had no derivation; 500 does -- ~800px viewport less ~120 header and
~150 controls leaves ~530, and the version that broke dragging was 620.
CB-WP-0021 T06's rule is to fix the measurement rather than lower the
floor, and an underived number is a measurement defect.

make all: exit 0. 66 render tests, 26 cb-play.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 09:54:20 +02:00

10 KiB
Raw Blame History

id kind title status state_hub_workstream_id
CB-WP-0029 product The tokens on the table: components you can count, and a supply the engine may not respect active 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 cardprotect 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 05 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

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, 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 05 track is a fact you see coming, and DARVO arms at Stress 5.

Task: tokens as objects

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 05 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 05 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?

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, 26 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

id: CB-WP-0029-T04
status: todo
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.