clay-borg/decisions/ADR-0016-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

5.9 KiB
Raw Blame History

ADR-0016: a token is a view, and the supply is not a rule we get to invent

status: accepted date: 2026-08-07 decided by: agent, under the standing loop authorization tier: M (structural M — imports another edition file under AM-4's budgets; chaos d8=4 → no override). Tier M merges survey and decision. references: CB-WP-0029, ADR-0015 (the import test and the hand reader), ADR-0011, GameDesign.md §1

Context

The engine models every token correctly and shows them as numbers on a seat card — protect 2, blamed by P3. Tokens.csv describes them as components, with a quantity and a sides count, and that raised a question worth measuring before deciding anything.

The measurement, taken first

750 games across 26 seats, greedy and random policies:

token supply max per seat max on table exceeded?
Protection 6 1 2 no
Denied 5 3 no
Relation link 12 12 exactly at the limit, never over
Focus / Blame 6 0 conflicts no

No supply violation exists in play.

And the first version of that check was wrong. It tested whether a seat had its own Focus placed and any Blame in blame_from — but blame_from lists other players' discs, so those 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 — caught before it left the repo.

The link row is the interesting one. Twelve tokens, two per player, six players: the supply is exactly consumed at full occupancy and never exceeded, because GR-L01 already enforces two slots per seat. The component count and the rule are the same constraint written twice — and that is the shape of a supply that does not need separate enforcement.


D1 — vendor Tokens.csv

ADR-0015's test: does it carry text a player reads? front_text (READY, DENIED, PROTECTION, FOCUS, LEAD, ROUND) and use do. shape, size and symbol_id are print instructions and are read by nothing here.

Vendored whole with a digest, as the others are — taking a column subset would mean a second decision every time a column becomes interesting.

D2 — a token is a view, not a type

The aggregate gains no Token. A token is a way of seeing state the aggregate already holds: the Stress marker is stress, the DARVO pawn is darvo, the Freedom disc is freedom_ready.

Adding a Token type would create a second source of truth for Stress, and the first time they disagreed the bug would be invisible — both would look internally consistent. This is INTENT's own rule: own the semantics, and do not duplicate them for presentation's sake.

So Tokens.csv supplies labels and counts for the renderer, and nothing in games_ground changes shape.

D3 — quantity does not bind, and the engine must not enforce it

Measured: never exceeded. But the reason for the decision is not the measurement.

A component limit the rules do not state is not a rule.

GroundRules.md derives 59 rules from the dataset and none of them mentions a token supply. If the engine began refusing a seventh Protection token, it would be enforcing a constraint nobody ruled — which is exactly the error CB-WP-0023 exists to correct, in the other direction: SOLVE was offered where it could not act, and this would refuse where the rules allow.

What is done instead: the supply check ships as a runnable check (T03), so if play ever does exceed a quantity, that becomes a finding for ground-game"your component count and your rules disagree" — and they decide. Which is a real question: a physical game cannot hand out a seventh Protection token, so either the rules bound it or the box does.

Not registered as a finding today, because nothing was found. A stated "none found" is registered instead, because a survey that reports nothing and leaves no trace is indistinguishable from one that was never run.

D4 — where each token sits, and why placement is the point

The overhead table (CB-WP-0028) is what makes this more than decoration.

token placement
Stress marker a 05 track at its seat
DARVO pawn an OFF→DENY→ATTACK→REVERSE track at its seat
Freedom a two-sided disc at its seat
Protection, Blame, Focus counted objects at their seat
Denied on its Problem, not as a word in a corner
Lead, Round on the table itself

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 triggers at Stress 5, so "one more Attack and I trigger" is the single most useful thing the page could show and currently does not.

Consequences

  • editions/ground-darvo-r0/Tokens.csv + digest; edition-check covers it automatically, since it enumerates what is present.
  • edition.rs gains a tokens() reader over the existing Table.
  • The renderer draws tracks and counted objects; games_ground is unchanged.
  • The supply check is committed and runnable, and its result is in the register as a stated negative.

What was rejected

rejected why
a Token type in the aggregate a second source of truth for Stress; INTENT forbids it
enforcing quantity a limit the rules do not state is not a rule (CB-WP-0023, inverted)
importing only some columns a second decision every time a column becomes interesting
leaving the supply unchecked "probably safe" is not an answer, and a negative that leaves no trace cannot be told from an unrun survey
keeping Stress as a number the track is what makes DARVO visible before it fires