vendor tool that covers what the gate checks
They ruled on all seven items the same day. Two were actionable here.
F28 RULED: points. Modes.csv MODE_COOP clarified upstream to say
"penalties apply to points, not card count"; mastery is now
total - blame - denied. A recorded scenario went red on it --
gr-e02-shared-ground pinned 0 (2 claimed CARDS - 1 - 1) and now expects
2 (4 POINTS - 1 - 1). The number moved because the rule was decided, not
because the engine drifted, and the scenario records both rulings; its
schema has no field for a second one, so both live in ruled_note with
`ruled` carrying the LATEST date.
F29 RULED not-intended and APPLIED upstream: SCN_02's suits re-tuned the
same day. The characterisation test is how we found out -- it pinned the
duplication, went red on the re-tune, and that red WAS the notification.
It now asserts every pair distinct, the stronger statement the
duplication had made unavailable. SCN_02 re-measures at 73 at 2p, not
67: its own board now.
F26/F30 ruled and recorded. F30's ruling incidentally confirms our
reading -- they name priority-2's suit as the first lever, which is the
difference we identified without having measured causation.
vendor-editions grew twice, both times because it covered less than the
gate it exists to satisfy:
- It refused to touch ground-darvo-r0/ on the reasoning that the
baseline is "a separate record". That was wrong within the hour:
ground-game clarified Modes.csv and `make vendor` reported a clean
sync while edition-check went red. A sync tool that covers less than
its check reports success into a red gate.
- Its two-block rewrite DETECTED which fence held which set and
preserved the arrangement -- faithfully preserving a swap an earlier
write had introduced, leaving each fence under a heading describing
the other. edition-check reads every sha256 line flat and passed
throughout: a document can be self-consistently wrong and green.
Order is now asserted, with a control that goes red on a swap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The modes were already implemented; nothing had ever COMPARED them. The
scenarios were not implemented at all: edition::deal has taken a
scenario_id since it was written and the only caller passed the literal
"SCN_01", so 15 of 20 Problem cards had never been dealt by anything.
The seam was the whole mechanism and it sat unused, with nothing red
because nothing asked.
Scenario is now state (serde default SCN_01, so all 26 recordings replay
unchanged), selected by preset `scn-03-4p` with `standard-Np` still
meaning SCN_01, and by --scenario/SCENARIO= accepting ids, numbers or
titles, validated against the edition rather than a pattern.
The threshold now comes off the Scenario card, closing F25's hardcoded
5/7/9. The first version of that control was worthless and mutation said
so: all four scenarios print 5/7/9, so reverting to the bands left it
green. Split threshold_from() so it can be handed a card that disagrees.
The header read `scoring CommonProblem` where the Mode card is titled
COMMON PROBLEM, PERSONAL EDGE -- the defect CB-WP-0034 deleted from the
move buttons, still standing on the line that says what winning means.
The coverage probe was matching that Debug output and went red when it
was fixed: third instance (CB-WP-0024, CB-WP-0034). Page now carries the
premise, the mode's rules text, and the tiebreak.
scenario-panel plays 4x3x3. Findings: SCN_01 and SCN_02 are the same
board (identical cells, pinned by a characterisation test); SCN_04 is
the hard board at 2p (52% vs 67/73%, the only deck needing two Repair);
and group success is EXACTLY equal across all three modes in all 36
cells, because greedy never reads state.mode -- filed F27, the two
competitive modes are scoring lenses over cooperative play.
F28: SHARED GROUND's mastery subtracts penalties from the claimed COUNT
where the mode card's shared score is claimed VALUE. Raised, not fixed;
scoring is ground-game's to rule on.
Also fixes design.py reporting a backticked path as no reproduction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CB-WP-0045 left "nothing explains what a scope does" open and gave a
FALSE reason: that scoping is our Variant so no printed sentence exists.
The H2 package ships Rules_Text.csv -- twenty-two passages of
player-facing rules -- and nothing in clay-borg had ever read the file.
A wrong reason for an open item is worse than an open item; it retires
the question. Filed as F26 against ground-game: a package that adds a
FILE is invisible where one that adds a column is not.
The scope rule now renders under the table in the edition's own words,
only when a non-global scope is in play, matched by heading rather than
row number, and absent (never paraphrased) if the edition drops it.
The trial log stamps its variant on the begin marker -- a session
property, not an eighth column -- read off state.variant rather than the
--variant flag, because a bare `state.variant = v` leaves H2 inert and a
flag-stamped log would put false provenance on real player words. An
unstamped log reports `unrecorded`, never `ground-darvo-r0`.
Six mutations, six red. The sixth is the finding: every trials.py fixture
built its marker out of BEGIN, so nine checks followed BEGIN away from
what hotseat.rs writes and stayed green while real logs broke. A fixture
built from the constant under test cannot test the constant -- the
control is now a literal, asserted from both sides.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
H2 is ground-game's answer to our H1 reading — that a flat +1 to every
seat is a solve-rate tax scaling with the number of Problems. Unclaimed
Problems now tick only the seats in scope: global (all), personal (the
owner), bond (the owner's Bond network over Bond edges only, degree 0
falling back to personal), assigned by hidden priority so 2p never has the
bond card in play.
T01: the package is vendored with digests, and H2's Problems.csv is r0's
with one column added and NOTHING else changed — checked, not assumed,
because the delta claims deal_and_thresholds unchanged and a silent
difference would make every H2-vs-baseline comparison a comparison of two
boards as well as two rule sets. Scopes are read from the column, not
derived from the priority in Rust: F25 exists because we hardcoded numbers
the edition already carried.
T02: owner and scope are new ProblemState fields, both Option and both
skipped when None, so a baseline state serialises without them and every
recorded scenario's hash is untouched — asserted on the JSON, not assumed.
with_variant() replaces the bare field write, because state.variant = v
would leave owners unassigned: a silently wrong game rather than a failing
one.
T03: every named defect is mutation-proven — traversing Rivalry edges,
applying stacking once, a degree-0 owner ticking everyone, personal
hitting everyone. The degree-0 mutation MISSED first: the fallback lives
inside bond_network and the mutation broke the None-owner arm instead, a
different branch. It stayed green until aimed at the path the test
exercises. A mutation that misses is not evidence the test works.
T04: ownership is not a permission. Filtering SOLVE to the owner turns it
red, which is the regression this task exists for — the engine had no
owner concept before T02 added one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reading Player_Mats.csv and Glossary.csv — which O4 had forbidden
declaring about until they were read — found a rule. Player_Mats'
choice_rule column IS GR-R03: at Stress 4-5 choose ATTACK or GROUND unless
you spend a ready Freedom token. The engine's stress_gated and
allowed_under_stress_gate match it exactly, and now have a tripwire.
So Ornamentation.md gains §1.3: the unit is a column, not a file.
Classifying Player_Mats as ornamentation would have thrown a rule away
with the colour swatches. A mixed artifact is normal — a player mat is a
rule printed on a decorated card in the box too. O4 is rewritten and
re-grounded from provisional to rule accordingly.
O5, O6, O7 declared with falsifiers. O7's is not theoretical:
back_design_id is unread in four vendored files, and hidden information is
what card backs are for.
CB-EV-0029 answers T04's question per rule rather than as a count — yes,
every checkable clause — and states the reading that result does NOT
support: it shows GroundRules.md was a faithful secondary source for the
clauses with behaviour to check, not that the engine is correct.
10 of 19 files vendored. Nine still unread, and Rules_Text.csv is the one
that matters: every clause checked here was checked against our own
transcription of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vendored DARVO.csv, Relations.csv and Scenarios.csv — the three unread
files that carry mechanism. The engine has implemented the DARVO sequence
and relation formation/breaking from GroundRules.md since the beginning,
and had never read the game owner's statement of them.
Every checkable clause agrees, and agreement is recorded rather than
noted: a survey that finds nothing and leaves no trace cannot be told from
one never run. The two hardest clauses to notice were already right —
Focus placed "even if the Attack was cancelled", and Focus removed when
the sequence ends before REVERSE.
The tests are tripwires, not derivations. The match was made by a person
reading prose, and that reading goes stale in silence when the prose
changes; each behaviour pins the phrase it was read from, so a reworded
edition goes red and asks for a human. Mutation-proven by rewording the
cancelled-Attack clause.
F25 raised, and it is the real yield: Scenarios.csv carries
threshold_2_players/3_4/5_6, starting_stress and round_track, and the
engine hardcodes all three — a match returning 5/7/9, stress: 2 at setup,
five rounds. They agree on all four scenarios. These are the most
contested numbers in the project; the whole 4/6/9 vs 5/7/9 episode turned
on them, and the engine has been right by maintenance coincidence rather
than by reading the file that owns them.
Also pinned: Problems.csv and Scenarios.csv both state the deal and the
engine reads only the first. They agree; nothing was checking.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RULED GROUND-WP-0004 (2026-08-03) asked for gr-e01 to be reinstated as an
import check: sum(point_value dealt) >= threshold FOR EVERY SEAT BAND.
Only the 2p half was done — gr-e01-threshold-reachable-2p covers the
tightest band, and a check at one band tests one value of the variable
that moves, which is the shape GameDesign §1.4 refuses.
every_seat_band_can_reach_its_threshold covers 2/3/4/5/6 and reports a
row-level table with Surface separated from each hidden priority, which
is engine ask 3 and §1.2's ruled shape — "12 in the file" is the wrong
premise that clause was written for.
Result matches ground-game's authoritative table: 6/9/12 available
against thresholds 5/7/9.
Mutation-proven, and the mutation is the original defect: count Surface as
one of the hidden slots and it fails with "2p: available 4 < threshold 5"
— the withdrawn 4/6/9 undercount, reproduced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
F18 was the only open finding clay-borg owns, the only register row
lacking a reproduction, and the only off-target metric. It is also
understated: it reads as display data, but among the 14 unvendored files
are DARVO.csv (mandatory_effect, advance), Relations.csv (formation,
breaking) and Scenarios.csv — rules the engine already implements from a
secondary source and has never checked against the primary one.
The reproduction records column reads AT THE ACCESSOR rather than counting
them from the source: a list beside the code would be a second copy of a
fact the get calls already carry, and grepping would over-count because
six column names are shared between vendored files.
The first version was wrong in this repo's signature way — it watched
Table::at only, so it called visibility, required_solution and point_value
unread when the engine reads all three through problems_of's own index
lookups. Correct about the accessor, wrong about the engine: the ADR-0018
family, committed inside the artifact built to measure it. Problems.csv
went 7/13 to 10/13 once the manual reader was recorded too.
F24 raised: solution_deck() is a Rust literal that never opens
Solutions.csv. It agrees today, which is the point — the engine is right
by maintenance coincidence rather than by reading. Role `default`, with a
test that goes red the moment either side moves.
open, lacking a reproduction: 1 -> 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
ADR-0015 and the import. F18's fix: "I don't understand the GROUND card"
was never a design gap -- the card explains itself in the edition and we
never imported the explanation.
THE MEASUREMENT IS THE DECISION, and the gap is bigger than "one file of
nineteen". Of the file we DID vendor, the engine reads 5 of 13 columns:
title, problem_text, front_rules, reveal_effect and unresolved_effect were
discarded at parse time. The cheapest part of this pass costs no new bytes
and was sitting in the repo for eight days. And SCN_01 is hardcoded at
lib.rs:1824 -- the edition ships FOUR scenarios and the engine has never
dealt three of them. Nobody had said so.
ADR-0011's revisit condition is measurably absent, so the dependency
argument does not get re-run: across Actions, Solutions, Modes and
Scenarios there are ZERO doubled quotes and ZERO embedded newlines. The
hand reader's only job is comma-in-quoted-field, which it already did.
Refusing csv on a measurement rather than on a preference.
Vendored Actions, Solutions and Modes -- the text a player reads. Not the
production artifacts (BOM, Print_Manifest, Back_Designs, Symbols). NOT
Extensions.csv, which names content the designer placed outside the core;
importing it would break the claim that this engine plays the edition as
printed. It is now known to exist, which was the real risk.
One Table reader with four callers, because a per-file copy is how a
parser acquires four subtly different bugs. The GROUND card now shows
"Regulate. Restore the frame. Decide." with its GR/OU/ND text on demand;
Problems show their own titles where a priority number used to be.
The load-bearing test asserts the text is a SUBSTRING OF THE VENDORED
FILE rather than equal to a Rust literal -- a test comparing against 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; the edition is the game's own data and the
shipped runtime now reads it. Test machinery and game content are
different things and only one of them is optional.
And edition-check was written for a single-file world: it compared the
first recorded digest against Problems.csv regardless of which file that
digest described. It now checks every file both ways -- a vendored file
with no digest fails, a digest naming an absent file fails -- and asserts
ADR-0015 D3's falsifier directly rather than trusting it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ADR-0011 decided it: vendor the CSV with a checked digest, read it with
a ~50-line reader, and let the hashes move.
The declaration's constraint was measured against the WRONG BUDGET. It
said a CSV crate costs 21,613 against AM-4a's 3,798 of headroom, '5.7x
over, settled by measurement'. But setup and problem_priorities are
cfg(scenarios) and are not in the shipped runtime at all, so AM-4a never
sees them. Against AM-4b, csv costs 17,651 against 19,742 -- it FITS,
with 2,091 to spare. It is refused anyway, on proportion: 89% of the
budget's remaining capacity to read 20 rows. The revisit condition is
stated (nested quoting, embedded newlines, multiple dialects).
GR-S01 now deals Surface + hidden 1..=k as ruled, with edition values and
suits. Measured: 6/9/12 available against thresholds 5/7/9 -- the game is
winnable at every seat count, which is what the maintainer could not do.
gd0001 is INVERTED, not deleted, and now also asserts the 6/9/12 so a
deal that is reachable for the wrong reason still fails.
Blast radius was scenario expectations, exactly as the ADR predicted: no
scenario pinned a hash and no bundle is committed. Six scenarios and two
unit tests updated, each with a note. gr-e01-threshold-unreachable-2p is
RENAMED to -reachable- and rewritten as the non-provisional import check
ground-game asked for by name. gr-e03's setup was restructured, not just
renumbered: with values 2,2,2 its personal-edge test would have tied
three ways and asserted nothing.
BLOCKING: AM-7 fails at median 0.845 against its 0.9 floor. Isolated
across three runs -- 3 problems + stand-in 0.97, 3 problems + edition
0.909, 4 problems + edition 0.845. State is BOUNDED (proven: identical
after 5k and 100k events), so this is not the unbounded-growth defect
AM-7 exists to catch; it is a bigger working set streaming a long log.
Whether AM-7's floor is still right for a larger aggregate is a spec
question and lowering it requires an ADR, so it is not being tuned here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>