# 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](../workplans/CB-WP-0029-the-tokens-on-the-table.md), [ADR-0015](ADR-0015-the-cards-own-words.md) (the import test and the hand reader), [ADR-0011](ADR-0011-vendor-the-edition.md), [GameDesign.md](../specs/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 2–6 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 **0–5 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 0–5 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 |