Thirteen challenges, five FATAL, all five conceded. Nothing had reached
ground-game, which is the only reason this is a correction and not a
retraction.
The worst: `Reactive` was not "greedy with one preference changed". It
differed in five, including SpendFreedom — ranked 95 unconditionally
against greedy's `95 if gated else 0` — so the seat burned its Freedom
token in round one of every game. A second change to the exact mechanism
under study, and every number in CB-EV-0031 was measuring it. The pass
claimed ADR-0018's one-varying-parameter discipline in its own workplan
while violating it. GreedyPolicy::rank is now public and the policy
delegates, overriding one match arm, so the control is structurally true.
Withdrawn entirely: "H1-B suppresses DARVO in the attacker". Disabling
H1-B under the corrected policy changes the arm count by exactly zero.
The pass hedged the wrong variable — it disclaimed "the number 2" and
defended "the direction", and the direction is what failed. The
supporting inference was invalid anyway: final Stress cannot show who
armed, because DarvoEnded resets the stage and REVERSE gives its owner -2.
Corrected: criterion 1 was failed on the greedy column while the pass's
own printed table showed 31-1000 arms in the other columns — the
selective-column move, in the file that names it. "Peak Stress was 1" was
a maximum over StressSet payloads, not held state (true: 2); the baseline
game count was 1,600 not 3,200; and "a reckless policy plays identically
to a careful one" is refuted by this repo's own rank-95 policy.
Inert controls replaced, each verified red against the reviewer's own
mutation: the baseline hash test compared two identically-constructed
states (serde(skip) on variant left 57/57 green); the `unchanged:` test
checked 3 of 7 entries and passed with SOLVE made illegal; H1-A's ordering
and H1-B's OU-cancel path had no test at all.
edition-check now covers catalog.yaml and rules_delta.yaml, whose digests
CB-WP-0038 claimed and never recorded — the review found it and reported
it unverified rather than absent, which was the right call.
Still open: H1-B on the DARVO extra-Attack path is untested, regulation.rs
still skips setup failures silently, and round-5 arms are counted though
they can never act.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CB-EV-0030 concluded H1's DARVO arm rate was still 0. That was true of the
panel, and the panel was greedy-family throughout. GreedyPolicy ranks
`Ground if gated => 100`, so it grounds the instant the stress gate bites,
Stress plateaus at 3, and the arm at 5 is unreachable by construction. "H1
does nothing" was really "H1 does nothing to a seat that already manages
its Stress" — and H1 was written for the seat that does not.
`reactive` is greedy with exactly one preference changed: GROUND demoted
below ATTACK. Under it, H1's criteria 1 and 2 are MET — DARVO arms 400
times per cell, ATTACK is chosen 3 times per seat per game. Criterion 3
fails harder: reactive wins nothing at any seat count.
The larger finding is about the baseline. Greedy and reactive play
IDENTICALLY under baseline, and peak Stress across 3,200 baseline games
was 1 — against a starting value of 2. The gate at 4, the DARVO arm at 5
and the Freedom token are all unreachable, and a policy built to be
reckless with Stress is indistinguishable from one built to husband it.
That is a deeper account of F17 than F17 has. Not raised as a finding yet:
it wants the plural panel first.
A constant was investigated rather than reported: darvo was exactly 400 in
every cell while atk scaled with seats. Six-player final Stress is
[5,5,4,4,4,4] every seed — H1-B holds the attacker at 4, below the arm,
and pushes its targets to 5. The self-soothe suppresses DARVO in the
aggressor and concentrates it in the attacked. The direction follows from
H1-B's arithmetic; the number 2 is partly an artifact of reactive's
first-legal targeting, and is labelled as such.
Still unreviewed: tier L review outstanding on CB-WP-0038, and nothing
here reaches ground-game until it runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ground-game packages hypotheses as selectable rules variants — a catalog,
a rules_delta.yaml, and prose — and their note is explicit that CSV text
alone is not executable here. So the kernel gains a Variant in game state:
in the state, therefore in the hash, therefore in the recording, because a
scenario replayed under a different variant would diverge silently.
Baseline is bit-for-bit what it was, asserted across seat counts and
seeds. A variant system that perturbs the baseline invalidates every
measurement this repo has.
H1-A and H1-B implemented from rules_delta.yaml and mutation-proven on
their own defects: "unclaimed" misread as face-up-and-unsolved, and the
attacker's Stress read after the attack's effects. Their `unchanged:` list
is asserted rather than trusted — that list is their claim about their own
experiment.
Measured, and three of their four criteria fail. DARVO arm rate is still
0 under greedy; ATTACK selection does not rise and falls for the rank-75
policy; group success collapses from 165/190/200 to 0 at 3/4/6 seats.
The mechanism is not the assumed one: greedy answers the pressure by
regulating, Stress plateaus at 3, so it never reaches the gate at 4 or the
arm at 5 — H1-A acts as a solve-rate tax and H1-B is unreachable under
competent play.
A harness defect was caught before the claim: sweep discarded refused
games silently and never reported its count, so "nobody won" and "nothing
played" printed identically. Reporting H1 as unwinnable on that basis
would have been the ADR-0018 family aimed at another repo's design. All
200 games ran in every cell; the zeros are real.
Chaos d8 = 8 — the window's first override, redrew L against a structural
L, so it changed nothing. Window 3 recorded in ChaosRollHistory.
NOT REVIEWED: tier L owes a separate-agent adversarial review, and no H1
result may reach ground-game until it has run.
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>
--pace speed|interactive, defaulting to Speed. Nothing reads it yet, and
that is the point: it is the seam clay-animate attaches to, and a seam is
cheap now where a retrofit would not be. A misspelt pace is refused rather
than defaulting, because quietly falling back to Speed would look exactly
like the renderer being broken.
I3 is asserted rather than intended: the same scripted game at both paces
must produce a byte-identical serialised recording and the same end state
hash. Mutation-proven — leak the pace into the seed and it fails with "the
recording differs by pace, so a renderer has become mechanism".
specs/OrnamentRegister.md carries four declarations. This reverses the
reasoning written in T03 earlier, which said the first declarations would
come from F18's unvendored files: instances already existed. Hand order is
what prompted the category, and "who deals" was the maintainer's own
example. O3 is the interesting one — seat ORDER is mechanism because
GR-R08 rotates Lead, while where a seat is drawn is not.
I5 is executable: check_ornament_falsifier fails any row still declared
that names no falsifier, mutation-proven red on O1. Presence, never
adequacy, and the finding text says so.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Draws one boundary — between what the rules can see and everything else a
player experiences — and says which repository each belongs to as the
simulator grows.
"Ritual" becomes ornamentation: half the category is material rather than
ceremonial, and "decoration" is already spoken for in this repo (a control
that cannot fail). The test is not "does it have a decision in it" —
choosing who deals is a decision and is ornamental. The test is whether
the state hash moves, which reuses the instrument that already binds a
trial note to a position.
The part that earns its place in clay-borg rather than in a renderer's
stylesheet: calling something ornamentation is a CLAIM THAT IT DOES NOT
MATTER, and this project's register is a list of times that claim was
wrong — quantity in Tokens.csv, F18's four unread files, SOLVE offered
where it could not act. So a declaration carries a falsifier, and
"provisional" is a state it must say out loud.
Downstream may read, may not decide — ADR-0007 D5 restated at the
repository boundary. The port to clay-animate is deliberately NOT
designed: no consumer exists, and an interface built against an imagined
client is the same defect as a gate that cannot go red.
Five invariants, four checkable today. I3 — same seed and decisions
produce a byte-identical recording at any pace through any renderer — is
the falsifier for the whole split.
CB-WP-0036 re-declared from L to M accordingly: the animation architecture
that made it L has moved to clay-animate. No code had been written and the
tier-L review had not been run, which is the only reason it could be
re-scoped rather than unwound.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tier L, and it is not the animation that makes it so. Three things in the
report are not presentation: a ritual is an interaction with no command
behind it and must never reach the recording; hand order is not game state
and must not move the state hash; and animating another player's move
needs the page to see a SEQUENCE, not a state.
The obstacle named up front: the page reloads after every accepted
command, which destroys the DOM, and you cannot animate across it. Letting
JS mutate the DOM instead is refused — that is ADR-0007 D5's line. Rust
describes the movement and the reloaded page animates into place, which
keeps the server authoritative and makes the animation a rendering of
facts Rust wrote, exactly like the log. The journal is already the
sequence needed.
The invariant that protects the rest: for the same seed and the same
decisions, the recording is byte-identical in both paces.
Left ready, not active: tier L requires a separate-agent adversarial
review before code, and it has not been done.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two reports, one cause: the script's fetch had no .catch. When the server
had exited the promise rejected, the chain never ran, and not even the
status line moved — so a dead server and a click that did nothing were
indistinguishable. play again read as a dead button, and the linger
timeout was invisible.
Now a rejection says the session is gone and seals the page, and a
5-second heartbeat against a new /alive notices it without needing a
click, which is what the timeout case requires. The beat carries the
token, and does not extend the linger: that deadline is absolute.
The harness had the same hole. jsrun's fetch stub had no .catch, so the
branch that notices a dead server would have been unreachable in every
test — the very defect the stub's own comment records from CB-WP-0024.
Teaching it __failing, .catch and a recorded setInterval was the fix;
writing the script defensively would have repeated the trap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reported three times across three sessions, two days apart, and it
survived a whole UI rebuild: "i cant see whos support i accept".
It was not styling. The move button's label was format!("{c:?}"), so the
player read `RespondToSupport { response: AcceptBond }` — Rust struct
syntax with no name in it. And the command does not carry the
counterparty, so nothing rendering it alone could have said who; it comes
off the view, as whoever played Support at this seat.
Four more seat-panel fields had the same defect, including `support
AcceptBond` — the one the report names. CB-WP-0020 fixed exactly this for
selections and left its four neighbours as they were.
command_label has no catch-all arm, and that earned its keep before any
test ran: GroundChoice::RejectReverse and SupportResponse::BreakRivalry
both failed to compile — two moves that would have shipped as struct
dumps. An offer the view cannot see is said to be unseen rather than given
an invented name.
The finding underneath: the coverage probe that exists to prove every view
field reaches the PLAYER was matching "player: Some(PlayerId(1))" and
"members: [PlayerId(1)". It was certifying the defect as coverage and
would have gone red had anyone fixed it. Second confirmation of
CB-WP-0024's finding, from the sharper side: a probe naming Debug output
does not merely fail to protect, it pins the defect in place.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
make trials reported "positions unreachable: 4, target 0 — the recording
exists but the position moved". All four were false. A recording holds one
hash, the final state, and reachability asked whether the note's hash was
in that file — so a mid-game note could never match, and a post-game note
from any but the last game could not either. Instance 8 of the ADR-0018
family: vary only WHEN a note was written and the answer flips, with
nothing having moved.
The root cause was not the metric. play again reused state belonging to a
game: it overwrote the previous game's recording (data loss), never
cleared the journal (game 2's log opened with game 1's commands), and so a
note's command index pointed into a recording without those commands.
Fixing reachability alone would have gone green while a session still
destroyed its own evidence.
A note now binds by (game, after) — an index into the recording's own
commands list, which a reader can replay to. The hash keeps a job as the
integrity check at the end of a game, where it can actually fail. Game 1
keeps the path it was given, so GameDesign §5's documented invocation is
unchanged; later games get -2, -3 and nothing is overwritten. Legacy
5-column logs stay readable and are reported as legacy, never as orphans —
an unsubstantiated orphan claim is the defect being fixed.
All three fixes mutation-proven, including at the call site via a real
two-game session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Notes were written to a file and shown back nowhere — the shape
trials.py's own docstring calls this project's signature failure in a new
medium. The log now carries the player's comments where they were made.
Position is the feature: a remark like "why did that do nothing?" is about
the move above it, and collected at the bottom it is a sentence with no
subject. round/step cannot order a note against the log because several
commands share a step, so the server records how many commands had been
played — which it knows exactly — and the page places it there.
A comment must not be readable as something the game did. The log is the
recorder's vocabulary; notes render in their own block, attributed to the
player, quoted. The .note CSS already existed and nothing had ever used it.
No column was added to the trial log. trials.py skipped any row that was
not five cells, silently, so a sixth column would have made `make trials`
report zero notes for every log at once. That latent defect is fixed on
its own terms: a wrong column count now raises, and the walk reports the
real reason rather than blaming a missing block for every failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The note channel closed at the moment it is worth most — a player who has
just seen the outcome is the one with something to say, and that reading
was collectable at every moment of the game except the one after it.
Two independent defects: doc::ending never rendered the box, and
serve_end had no POST /note arm, so even a hand-built post fell through to
404. Fixing either alone leaves the channel shut, so the test asserts both
and is mutation-proven to fail on each half separately.
A post-game note binds to the final position but is not an observation
made at the last decision point. RoundStep::End is the last step of a
ROUND, not the end of the game, so record_note now takes the step as an
argument and the post-game path passes "after the end" — otherwise an
after-the-fact reading is filed as an in-play one, which is the
wrong-subject family ADR-0018 was written for.
A note does not end the session: every other POST in that loop breaks it,
and a player must be able to write a second one and then still play again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven claims in this project have been wrong the same way — the arithmetic
right, the subject wrong. Four reached a document or another repo.
ADR-0018 states the rule: a measured claim names the variable it depends
on and shows the measurement moving with it. GameDesign §1.4 adds it as a
fourth admissibility clause; the existing three are untouched.
Honest yield is 3 clear + 1 partial of 7. It misses a count never made and
a claim that is not a number, and D3 names those so nobody stops looking.
The tempting fix — closing facts-check's 62 untagged literals — would have
caught 1 of 7 and is deferred as separate work rather than bundled.
loop-lint gains check_sensitivity_stated over arithmetic register rows,
mutation-proven red on F17 and green when restored, with four self-test
controls. It checks presence, never adequacy, and says so in its output.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Declaration 1 of chaos window 3 (opened by ADR-0017), tier M, chaos d8=7,
no override.
Seven claims in this project have been wrong the same way, and the
workplan tabulates all seven with what caught each.
MEASURED BEFORE PROPOSING: facts-check reports 62 untagged literal copies
and fails on none, which is a real drift surface it names and does not
close. CLOSING IT WOULD HAVE CAUGHT AT MOST ONE OF THE SEVEN. Instance 1's
denominator WAS a registry fact and WAS correct -- the error was that
AM-4a does not cover the code. Four others were fresh measurements with no
registry key. A tag proves a number came from somewhere; it cannot prove
the somewhere was the right place. Named as separate work rather than
bundled, because bundling would let this pass claim a victory it did not
win.
What did work: every instance was caught when the number was computed a
SECOND TIME by something with a different purpose. Two of them share a
sharper property -- the claim was tested against a variation. #4's cost
FELL as branching ROSE, which no per-node cost can do, and the tell was in
the published output. #5's win rate was attributed to the game, and a
second policy on the same deals gave 0% where the first gave 100%.
So the rule generalises what this project already builds after each
instance (the policy panel, the attack-value sweep) and requires it
before: a measured claim must name the variable it depends on and show the
measurement moving with it. A number that does not move when its subject
moves is measuring something else.
HONEST YIELD: 3 or 4 of 7, and it catches the two that travelled furthest.
It does not catch a count never made, or a claim that is not a number.
Claiming it closes the family would be the family's own error applied to
itself.
T03 requires the lint to fail on a real row by mutation, and to say in its
own output that it checks PRESENCE and not ADEQUACY -- so nobody reads a
green lint as 'the claim was verified'.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
not
Tier M (changes how the loop constrains its own operation), declared at
d8 because the rate for window 3 is what this document decides and
declaring at a rate it invents would be circular. chaos d8 = 7, no
override.
I CLAIMED WINDOW 2 PRODUCED ZERO OVERRIDES, FIVE TIMES, AND IT IS FALSE.
Declaration 3 (CB-WP-0020) rolled d8 = 8, overrode, drew S against a
structural S, and changed nothing -- and CB-WP-0020 recorded it correctly
at the time, in those words: "the first override at d8... It changed
nothing... One."
Counting the workplans takes one command and I never ran it. CB-EV-0024
asserted "zero" without checking; CB-EV-0025, 0026, 0027 and CB-WP-0029
each cited the one before. A claim propagated five times by citation
rather than by measurement, in files whose subject was that exact failure.
facts-check catches a copied number that disagrees with its source;
nothing catches a number with NO source, asserted once and repeated.
Registered F23, and all four evidence files carry an in-place correction
rather than a silent edit (ADR-0012 D5).
THE ACTUAL VERDICT: THE RATE IS WORKING. Eleven rolls at d8 -- declaration
1 opened the window at the old d4 -- against 1.375 eights expected, 1
observed. Not a shortage of evidence; the design.
BUT THE RETIREMENT CONDITION GENUINELY CANNOT FIRE, and that took
computing to see. "An override changes nothing twice running" needs a
consecutive pair at P=1/3 each, so ~12 overrides expected, at ~1.4 per
window: ~9 windows, roughly 100 declarations. A gate that cannot cash out
on any realistic horizon is decoration, which ADR-0006 D3 forbids.
Restated to be evaluated PER WINDOW: retire if a full window's overrides
all change nothing, met in two consecutive windows. A window with no
overrides is inconclusive and advances nothing. ~24 declarations rather
than ~100. Window 2 counts as the first; window 3 opens at d8 and decides.
Recorded and deliberately not acted on: declarations 5-9 rolled six five
times running, ~1 in 370 for some run of five in eleven rolls. shuf tested
over 200 rapid successive calls looks uniform, longest run three. Found
post hoc, which is how coincidences become findings, so it is logged for a
future window to check rather than treated as evidence.
InnerLoop.md then crossed the loadability limit, and so did
InnerLoopReference.md. The window log moved to specs/ChaosRollHistory.md:
it grows by one entry per window, and a log inside a reference eventually
crowds out the reference.
make all: exit 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
unplayed was ours
Tier S (a fix and a measurement inside a boundary; chaos d8=4 from
CB-WP-0029's roll, no override).
cb-play built EVERY game with ScoringMode::SharedGround and passed an
empty patch. The mode was settable in scenarios and not from the driver,
so two of the three shipped modes were unreachable from the only way
anyone actually plays. F14 sat open for a week because nobody could reach
the thing it was about.
--mode added. All three now play out and give DIFFERENT WINNERS FROM
IDENTICAL PLAY: shared -> all four seats (mastery 4), common -> P3 alone
(top personal scorer), coalitions -> P1+P2 (best Bond network, 4>3>2).
Same 37 commands, three answers.
AND THEY ANSWER F17'S OPEN QUESTION. I had flagged that ATTACK might earn
its place where Blame costs personal score. It does not, in any mode:
SHARED GROUND 132/165/190/200 -> identical free but pointless
COMMON PROBLEM 59/52/48/44 -> 59/52/48/34 a cost at six seats
BONDED COALITIONS 131/134/132/116 -> 59/52/48/34 roughly halved
The coalitions row has a mechanism and the data confirms it unprompted.
GR-A07 flips a Bond to a Rivalry on Attack, and GR-E04 scores Bond
NETWORKS -- so attacking destroys the thing that scores. And the attacking
numbers in E04 are IDENTICAL to E03's, which is exactly what that
predicts: break every Bond and each seat is a coalition of one, so GR-E04
degenerates into GR-E03. That check was not designed; it fell out.
F14 -> applied. F17 strengthened and no longer bounded to co-op: ATTACK
has no mode in which it helps, and one where it actively destroys your
score.
Still framed as a question rather than a verdict. DARVO is the pattern the
game is about not falling into, so a self-destructive ATTACK may be the
design. What ground-game has to decide is whether the namesake mechanic
being unreachable in competent play -- in all three modes -- is intended.
make all: exit 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
says
Five games per seat count, 2-6 players. NO ANOMALIES: every game reaches
5 rounds with an outcome, no stalls, no stress above the cap, no
over-claimed Problems.
But the series showed something a crash never would. DARVO NEVER FIRED IN
25 GAMES and stress never exceeded 2. Measured wider: GreedyPolicy plays
ATTACK exactly ZERO times in 10,000 selections across 500 games.
THAT NUMBER IS ABOUT OUR BOT, NOT THE GAME. bot.rs ranks
`Action::Attack => 10`, below everything. Reporting "the game gives no
incentive to attack" from a policy we programmed to rank attack last would
have been CB-WP-0025's C4 error committed again -- a single policy's
behaviour presented as the game's.
So the artifact varies exactly one number: ATTACK's rank in an otherwise
identical policy, 200 games per cell.
rank 10 (below all): 132/165/190/200/200 wins, 0 attacks, 0 DARVO
rank 75 (above SUPPORT): 132/165/190/200/200 wins, 315-923, 13-218
rank 95 (above SOLVE): 0/0/0/0/0 wins, 1400-5170, 400-1000
THE MIDDLE ROW IS THE FINDING. Identical win counts at every seat count,
while attacking hundreds of times and arming DARVO repeatedly. Attacking
is not punished -- it is INERT with respect to the goal. Group success is
a function of SOLVE alone, and ATTACK costs anything only when it ranks
above SOLVE and displaces it.
The maintainer was right and the reason is sharper than his phrasing:
there is no incentive because there is no PATH. ATTACK's effects (Stress,
Rivalry, DARVO) feed nothing that decides group_success.
Bounded honestly to SHARED GROUND. Blame costs PERSONAL score, so ATTACK
may earn its place in GR-E03 and GR-E04 -- which have never been played to
the end (F14), and that is where to ask next. And this is NOT a claim the
game is broken: DARVO is the pattern the game is about not falling into,
so a self-destructive ATTACK may be the design. The question for
ground-game is whether the namesake mechanic being unreachable in
competent co-op play is intended.
F17 promoted from note to raised, with games/ground/examples/attack-value.rs
as its reproduction. Register: 18 findings, 8 with a resolving
reproduction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
Declaration 12 of chaos window 2, which CLOSES IT. Structural M (imports
another edition file under AM-4's budgets), chaos d8=4, no override.
Tokens.csv turns out to carry more than names. Every token has a
`quantity` -- 6 Protection, 5 Denied, 12 relation links -- and a component
count is a limit a simulation does not have unless it is told. protection
is saturating_add(1) at lib.rs:986 with no upper bound, against an edition
that ships six tokens for six players. T03 measures whether that matters
rather than assuming either way.
And `sides` is the second interesting column: Freedom and Focus/Blame are
each ONE DOUBLE-SIDED TOKEN, 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.
T03 is shaped against a specific error: whatever it finds is a FINDING,
not a fix. A component limit the rules do not state is underdetermined and
belongs to ground-game, and 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.
The workplan also names the window-2 verdict as DUE. Its retirement
condition was untestable throughout -- eleven declarations, zero 8s, zero
overrides -- and recording that is a change to how the loop constrains
itself, so it is its own tier-M pass rather than a task here. Named so it
is not lost between passes, which is how the ten rulings went uncollected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tier S (fixes inside a boundary; chaos d8=3, no override). Two
observations from play, both about what the page says has happened.
The controls moved BELOW the log: you read what happened, then decide what
to do next. They were above it, which asks for the decision first.
And sealing now marks the WHOLE PAGE inert, not just the controls. A
greyed-out button beside a full-colour table still reads as a live game
with one broken control; the session has ended and everything on screen is
a record of it. The status line stays legible on purpose -- it is the one
thing still worth reading.
THE STUB NEEDED A BODY classList TO MAKE THAT TESTABLE. Without it "the
session visibly ended" would have been a claim about CSS with nothing
checking it, which is precisely CB-WP-0016's finding: a stub too thin to
express a failure is how the failure survives. The harness reports the
sealed page through the status channel with a NUL-separated marker --
ugly, deliberate, and documented, because widening the return type would
touch every caller for one boolean.
Both directions asserted: a `closed` reply seals the page, an `ok:
dealing` reply does NOT -- otherwise the seal test would pass for a page
that greys itself whenever it is touched, breaking `play again`.
63 render tests, 26 cb-play, check and loop-lint clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tier S (a fix inside a boundary; chaos d8=7 from the previous roll stands
for this continuation). Two observations from play that are ONE defect.
`play again`, `end session`, `pass` and the move buttons carried `.pick`,
which is cursor:grab. The stylesheet has .btn{cursor:pointer} BEFORE
.pick{cursor:grab}, so grab won.
A GRAB CURSOR INVITES A DRAG. A drag released over nothing posts nothing,
so the player picked up the button, let go, and the page did nothing. It
looked dead because the affordance told them to do the one thing that does
not work. Reported as two separate things -- "the button shows a hand to
pick up that it probably shouldn't" and "I can't start another game or
stop the server" -- and the first causes the second.
The click path itself was never broken: driving again->again and
done->done through the JS harness posts correctly. The logic was fine and
the invitation was wrong.
Click targets now carry `.tap` -- pointer cursor, same press affordance.
This extends CB-WP-0017's rule (interactive and inert must not look
identical) to: click and drag must not look identical either. The test
asserts both directions, because checking only that buttons lost `.pick`
would pass for a page with no affordances at all.
Registered F20 (applied) and F21.
F21 IS THE ONE I COULD NOT REPRODUCE: dragging did not work until after
the first note was saved. Ruled out the plausible mechanisms -- the
gesture logic posts correctly against the served page, the drag ghost
carries pointer-events:none so it cannot intercept the drop, and the
markup is identical before and after since the 303 re-renders the same
page from the same state. Remaining candidates are a <details> toggle
shifting layout mid-drag, a first-load timing difference, or browser-level
pointer capture. Reproducing it needs a browser, which no test here has --
the same gap F19 named. Recorded as unreproduced rather than given a
speculative fix.
And the fourth observation is confirmation, not a bug: "drawing my cards
from the deck is not implemented, I did not need to do that" is exactly
what CB-WP-0028 T04 determined and deliberately did not build. It is the
first evidence that importing the card text closed the comprehension gap
that produced the earlier click-the-deck request.
make all: exit 0. 62 render tests, 26 cb-play.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CB-WP-0028's overhead view was 620px tall, so the action cards sat a
screen below the Problems and dragging between them was physically
impossible. Seats were drawn inside the table; the `table` drop target was
a card among the buttons rather than the drawn surface.
make all passed throughout. Every test asserted the DOM was correct --
which it was -- and the JS harness posts a correct gesture against a page
a human cannot drag on.
Kind `degenerate`: it fires and collapses play. State `applied`: fixed the
same day, with height, seat-position and single-drop-zone proxies added.
They are PROXIES -- nothing here lays out a browser, and the gap CB-EV-0026
named a day earlier is unclosed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tier S (fixes inside a boundary; chaos d8=7, no override). Four
observations from play, three of them caused by CB-WP-0028 -- and make all
was green for all of them.
THE NOTE BUG, WHICH I GOT WRONG TWICE. The first fix put the token in the
form's action, and that worked. But the 303 afterwards pointed at bare `/`
with no token, so the note WAS SAVED and then the browser followed a
redirect control 1 refuses. The player sees "no session token" for a note
that already landed. A redirect is a request the browser makes on your
behalf and is subject to every control the others are. I had tested the
POST and stopped there -- the same mistake as the first fix, one step
further along. Guard::page_path() now carries the token, and the test
asserts the redirect target is ADMITTED rather than merely non-empty.
WHY DRAGGING BROKE, WHICH WAS NOT THE DRAG. The gesture logic was fine: the
JS harness posts correctly against the served page, and all 14 drop
targets are present. The table was 620px tall, which pushed the action
cards a full screen below the Problems -- and you cannot drag between two
things that are never on screen together. Now 440, with a test asserting
the declared height stays under 460 and saying why. That is a proxy for a
browser layout, not the property itself, and the test says so.
Seats sat ON the ellipse: they were placed at 0.83 of the table radius,
which is inside it. Now outside, asserted numerically at 2 through 6 seats
against the ellipse equation rather than eyeballed.
And the `table` drop target was a separate CARD among the move buttons,
which is exactly why a player looking at a picture of a table could not
find anywhere to drop. The drawn ellipse is the drop zone now, and a test
asserts there is EXACTLY ONE table target and that it is the drawn one --
two elements claiming to be the table is worse than none.
The gap this exposes is the one CB-EV-0026 named a day earlier: every test
asserted the DOM was correct, and it was. Nothing asserted the page was
usable, and the drag test passes on a page you cannot physically drag on.
What is added here are proxies a browser-less test can check.
make all: exit 0. 61 render tests, 26 cb-play.
Verified over real HTTP rather than by inspection: note POST 303, the
redirect carries the token, following it returns 200, the table declares
440, one drop zone, seats at (380,421)/(113,112)/(647,112) against a table
of rx=200 ry=118, and both notes reached the trial log.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
turned out to be
T03. One table_svg: seats around an elliptical table starting at the
BOTTOM -- the viewer sits nearest the reader, as at a real table --
Problems and both stacks in the middle, each seat's played card between it
and the centre, relations drawn between seats.
Two renderers were DELETED: relations_svg and piles_svg. The task said one
table not two diagrams, and leaving the old ones would have meant drawing
the same thing twice and letting them drift.
No coverage probe cost, through a restructure that merged three diagrams
and removed two functions. Second confirmation of CB-WP-0027's finding: a
probe naming a FACT survives a reflow, one naming a PRESENTATION does not.
CB-WP-0024's "17 remaining" broke on a rendering change; this far larger
reflow broke nothing.
The new control is per seat count -- no two seat circles closer than 70px
at 2 through 6 -- asserted rather than eyeballed at three, which is the
only count anyone ever looks at.
T08 (CB-EV-0026). Seven of nine observations were engine defects, one was
a design finding, one was already true and nobody could tell.
Observations 4 and 5 both dissolved and had ONE cause: nothing on the page
said how drawing works, so a player built a mental model to fill the gap
and reported the gap as two feature requests.
The import gap was worse than "one of nineteen" -- 5 of 13 columns read
from the file we DID vendor, discarded at parse time for eight days. Rule
coverage was 59/59 throughout. The gate measures whether rules are
EXERCISED; nothing measures whether a player can READ the game, and
nothing cheaply could, which is why the person playing it is the
instrument.
TWO GATES WERE WRITTEN FOR A SMALLER WORLD, and neither was wrong when
written. edition-check compared one recorded digest against Problems.csv
regardless of which file it described -- correct with one vendored file,
comparing across files with four. And a cb-play test asserted the literal
"game over" and went red when a won game said "solved", which was T06
working; it now asserts the heading against the OUTCOME and covers the
no-outcome case the original never touched.
Chaos window 2 closes with zero overrides in eleven declarations at d8.
Third and final statement of it: d8 bought rarity by spending evidence,
and a mechanism producing no data across a full window cannot be evaluated
by that window.
make all: exit 0. 57 render tests, 26 cb-play, loop-lint clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a feature deliberately not built
T04 BUILT NOTHING, WHICH IS THE CORRECT OUTCOME. "Click the draw stack to
get hand cards" is not a legal move: GroundCommand has no standalone draw,
and the edition's own INVESTIGATE text settles it -- "Choose one hidden,
non-Denied Problem and reveal it. THEN DRAW ONE SOLUTION." Drawing is a
consequence, never an action. Implementing click-to-draw would have
invented a rule, which is what CB-WP-0023 exists to stop.
And observation 5 is already true: drawing is ALREADY automatic, inside
INVESTIGATE, with no player input. An auto-draw option was asked for a
thing that has never been manual.
Both observations have one root cause and T02 fixed it. The maintainer
expected to take cards from the deck because NOTHING ON THE PAGE SAID HOW
DRAWING WORKS -- the INVESTIGATE card's own text was in a file we had not
imported. No finding raised: a player's instinct differing from a legible
rule is a comprehension gap, not a rules gap. Whether the instinct recurs
now that the text is present is a testable question and was not before.
T06: "game solved" on a win, "game over" on a loss, "the game stopped"
with no outcome. Asserted all three ways, because a test checking only the
win case passes for a page that always says solved. More than tone --
GROUND is co-operative and about repairing something, and arcade
failure-state vocabulary for a win tells a player the wrong thing about
what they just did.
T05: the ending page gets the table's two-column shape. Result left;
rankings, controls and the full log right. The seal still removes every
control wherever they now live.
T07's interesting decision was NOT TO RANK. Modes.csv defines
scoring_tiebreak per mode, so ordering is the GAME's where one exists --
"Lower combined Stress, then fewer Blame tokens" for coalitions. For
SHARED GROUND it says "Not applicable", because the table succeeds or
fails together. So co-op shows contributions and refuses to order them,
and says why. Drawing a leaderboard because a leaderboard is easy would
invent scoring the rules do not have. The one derived superlative is
labelled "clay-borg's reading, not a rule" and appears only where the mode
ranks; ties are shown as ties.
52 render tests pass. check clean, loop-lint clean.
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>