Commit graph

212 commits

Author SHA1 Message Date
561facce43 CB-WP-0022 T05/T06/T07: the register, and what its first run found
Some checks failed
ci / check (push) Has been cancelled
T05. tools/design.py, make design, and the register in GroundRules.md --
14 rows, no new file, because ADR-0012 D2 made §Underdetermined the
register rather than building one beside it.

Backfill was the test and it caught two things the ADR did not have.

First, a `role` column. The first report alarmed on U2 and was wrong to:
U2's scenario is green BECAUSE the provisional default it documents is
implemented, which says nothing about whether ground-game agrees. GR-E01's
was a counterexample that went green. Same colour, opposite meaning -- a
register that cannot tell them apart either alarms constantly or never.
Only a green counterexample alarms. Folded back into GameDesign §1.3.

Second, it contradicted the survey. CB-RES-0007 said "six of the ten
already have provisional scenarios." Measured -- grep -lE "\bU<n>\b" over
scenarios/ground -- exactly ONE U-item names itself. Five provisional
scenarios exist and four probably encode U-item defaults, but the mapping
is not written down, so it is not checkable. Same defect class as the
wrong premises, found inside the survey that proposed the fix. Now a
reported debt: open, lacking a reproduction: 9, target 0.

design.py carries the control design-baseline.py never had, asserted
directly: a row citing a nonexistent file must not count as reproduced,
using the exact path 2da19a4 deleted -- which the old tool called green.
design-baseline.py is marked superseded rather than deleted; it is the
evidence for how a wrong number got into a survey.

T06. The report is a FILE in ground-game under GROUND-WP-0002, committed
there, with a hub message that only points at it. It asks for no ruling:
it carries GR-E01's withdrawal, our reproduction debt, and two notes that
are explicitly not findings.

And it had to acknowledge something nobody anticipated. GROUND-WP-0002 is
finished -- all ten U-items were RULED 2026-08-03, every one confirmed as
the default we simulate, plus five of six provisional scenarios. The
survey said "0 of 10 ruled" two days later and this register was built
saying `reported`. That is the unread-inbox failure running in the
opposite direction: they answered and we did not collect it. The
instrument's first run surfaced it. They are `ruled`, not `applied` --
lifting the now-settled provisional flags is owed and is not done, and
make design shows them open until it is.

T07. evidence/CB-EV-0021. Two of six catches in this pass came from
execution rather than process (the role distinction from building it, the
ten uncollected rulings from running it), which is InnerLoop §Design
goal's prediction holding.

make self-tests, facts-check, loop-lint: clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:22:33 +02:00
5092684de5 CB-WP-0022 T04 follow-up: trim the workplan back under the loadability limit
loop-lint flagged 429 lines against the ~400 limit, and it was right about
the cause: the T02/T03/T04 completion records restated content that
ADR-0012, GameDesign.md and the challenge/response trail already carry.
Trimmed to pointers plus the one sentence each that is not written down
elsewhere.

400 lines, loop-lint clean. No content lost from the artifacts that own
it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:09:28 +02:00
b4f51effad CB-WP-0022 T04: specs/GameDesign.md -- what a reproduction must show
Not a register; ADR-0012 D2 put that in GroundRules §Underdetermined. This
spec says what may go in it, what a reproduction must show, how a finding
dies, and how a trial game is run.

§1.2 is written against evidence rather than principle. A finding must
print the rows behind any number it claims, and the spec carries the table
of what shipped instead: a sum ("12 in the file"), a green scenario
("4/6/9 against 5/7/9"), and a condition named without checking which one
fired ("SOLVE on a face-down Problem"). "12" was arithmetically defensible
and still wrong about the game -- that sentence is the requirement.

§1.3's target is 0 reproductions that have gone green while open. GR-E01
would have tripped it four days before a human caught it by hand.

No baseline rate is quoted. The 33% was withdrawn by C2 and the first
honest denominator is T05's backfill; quoting a new number from a
discredited instrument is how the first one got in.

The trial protocol costs one flag: cb-play --record already writes a
finished game as a scenario, so a trial is that plus a sibling .md in the
player's own words. An observation is a NOTE until it has a reproduction,
and notes may not cross the repo boundary and expire at 30 days on the
existing provisional-age machinery. The maintainer's "I felt it was too
easy but then we lost" is the case the protocol is shaped around --
forcing it into a schema at the moment of observation would lose it.

loop-lint: no findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:07:38 +02:00
580081ef71 CB-WP-0022 T03: ADR-0012 -- the register already existed, and the rule was
one clause short

Nine decisions. Two were not on T03's list; both are the review's.

D2: specs/GroundRules.md §Underdetermined IS the register. C4 pointed out
it was never evaluated as a candidate, and against the survey's own five
benchmarks it already delivers four -- including the Magic Oracle property
("a ruling flips the scenario, not the kernel", :231-233) that the survey
travelled to Magic to discover and we had written down ourselves eight
days earlier. What it lacks is reproductions. So this pass extends a
section rather than building a register: no new file, no new schema, and
no second mechanism to disagree with the first.

D3: admissibility is three clauses. It exists; it has the ruled shape
(GROUND-WP-0004 T02's row-level table, never a sum -- promoted from a T04
addendum because two of three wrong premises were sums without tables);
and it CAN FAIL. The third is C1's. GR-E01's scenario went green when the
edition landed, and the finding stayed admissible and stayed queued for
transmission, because nothing in the rule said a passing artifact was a
signal. A green reproduction is an alarm, not a reassurance.

D1 applied: INTENT gains a fourth property, Instrument, worded as a
mechanism rather than an ambition and carrying its own falsifier -- if a
pass tolerates an undecided rule by quietly picking a default, the
property is false.

D4 five kinds, each forced by an existing finding; a sixth during backfill
means the taxonomy was invented. D5 lifecycle where `applied` means the
source changed, the queue empties while the log accumulates, and
withdrawals are reported rather than deleted -- GR-E01 is why. D6 notes
admitted but never reportable, 30-day expiry on the existing age
machinery; refusing them would discard the only class of finding the
engine cannot produce itself, which is CB-WP-0025's whole input. D7 no
engine-evolution register, on an inventory C5 corrected -- narrowed, not
settled. D8 design-baseline.py retired, kept as a dated snapshot because
deleting it erases the evidence for how 33% got in. D9 the artifact stays
here, ground-game gets a generated file under its own workplan.

loop-lint: no findings. facts-check: no findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:05:51 +02:00
04c3a4977f CB-WP-0022 T02: the separate reviewer found the showcase finding was false
First adversarial review in this repo run by a genuinely separate agent.
CB-RES-0006's reviewer opened by conceding it could not be, and called its
own findings "a lower bound on what a genuinely separate reviewer would
find." This is the measurement: the separate reviewer ran git log against
the survey's central example and found 2da19a4 had falsified it four days
earlier, while the author -- who wrote that commit -- quoted the dead
number twice.

Seven challenges: four conceded, two conceded in part, one answered.

C1 changes the design. "GR-E01 is admissible because 4/6/9 against 5/7/9
is a computation anyone can rerun" was a computation already rerun: the
edition import measured 6/9/12, the conclusion inverted, and the scenario
was renamed -unreachable- to -reachable-. That finding was one of the TWO
that passed the reproduction rule. So three wrong premises have now
reached ground-game and the third satisfied an existence test -- existence
is not the property that was missing. The rule gains shape (ground-game's
row-level deal table, promoted from a T04 addendum) and a clause the
survey never contemplated: a reproduction must be able to fail. Ours went
green and stayed admissible.

C1 also caught a defect in flight. T06's payload, status todo, still named
4/6/9 and was queued to send it to ground-game as "no dataset reconciles
them." Withdrawn before sending -- the fourth wrong premise, and the only
one stopped.

C2 withdraws the baseline's precision: design-baseline.py is a
hand-maintained dict counting itself, has_reproduction never checks the
file exists (its YES-control is green against a deleted path), and
Makefile:127 runs only --self-test so the reporting path has no CI. The
direction survives; 33% is not a measured rate and T05 must not build on
it. C3: "six provisional defaults" is five, GR-E01 double-counted. C4:
GroundRules §Underdetermined was never evaluated as a candidate and
already delivers four of five benchmarks -- T03's burden flips to arguing
extension over replacement.

Survived: the rule's affordability, and reuse of the provisional
machinery.

loop-lint: no findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:03:12 +02:00
129ed03492 Sync hub state for CB-WP-0024/0025
Workplan ids and task ids written back by fix-consistency C-06;
WORK-RECORDS.md regenerated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 13:00:02 +02:00
custodian-sync
2b9fbf5fd3 chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 4s
Updated by fix-consistency on 2026-08-05:
  - update .custodian-brief.md for clay-borg
2026-08-05 12:59:54 +02:00
8e9b3c19b7 CB-WP-0024/0025: what play reported, split into a renderer and a search
Some checks failed
ci / check (push) Failing after 4s
Five remarks from the maintainer's test games, checked against the code
before being written down — two had already reached ground-game on wrong
premises, so a claim now names the line that makes it true.

Three of the five turned out to be data the projection already carries,
drawn as text: solution_deck_len, solution_discard, and OutcomeView's
personal/mastery/winners. One control (`close — I have read this`) is
labelled as a reading but shuts the server down, and leaves a live-looking
page pointing at a dead port. One number does not exist at all: table.rs
loops run_game and keeps only the last summary.

CB-WP-0024 (S, chaos d8=6, declaration 7 of window 2) — the piles and the
other seats' plays as objects on the table, the ending control saying what
it does, and a tally that survives "play again".

CB-WP-0025 (L, chaos d8=6, declaration 8) — "could we have won" and "how
hard is this" are the same search asked twice. Tier L because the
information boundary is the whole design problem: a solver reading
GroundState sees the deck the rules hide, and would tell the maintainer he
could have won by playing a card he had no way to know was there. Also
unblocks GROUND-WP-0005, active with both tasks waiting on a measured
difficulty baseline.

Also: CB-WP-0022 T07's evidence file moves to CB-EV-0021 — CB-WP-0023
shipped CB-EV-0020 first.

loop-lint: no findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 12:59:08 +02:00
43f94db42e Sync hub state for CB-WP-0021
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 01:11:30 +02:00
custodian-sync
f0a4b1fc60 chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 4s
Updated by fix-consistency on 2026-08-04:
  - update .custodian-brief.md for clay-borg
2026-08-04 01:11:17 +02:00
503966bce9 CB-WP-0021 T06: fix AM-7's measurement, not its floor
Some checks failed
ci / check (push) Has been cancelled
The row folded a 5,000-event log against a 100,000-event log and compared
throughputs, which confounds 'does cost per event grow with history'
(the property it claims) with 'does streaming a 20x longer Vec cost more
per element' (a memory-hierarchy fact true of any program). It measured
the second and reported it as the first: importing the edition enlarged
the aggregate and the ratio fell to 0.845 with the state bounded.

Corrected to time the SAME 5,000 events on a state at depth 0 and on a
state at depth 100,000. Equal windows, equal event mix, so the only
difference left is history depth.

  corrected: clean 1.004, mutated 0.589 (red)
  old:       clean 0.845 (red on healthy code), mutated 0.751

It also runs in 8.5s instead of timing out: the first version re-walked
the 100k prefix every repetition, 200M untimed folds per sample, which
under the mutation never finished. A control that cannot be run is not a
control. It now advances to depth once per sample and clones.

Two of my own measurements here were wrong and both were caught by
measuring again. A 2-minute timeout killed the shell line before its
restoring cp ran, so three readings were taken on MUTATED code -- I
diagnosed an event-mix confound that did not exist and 'fixed' it. The
fix is kept on its merits; the justification was fiction. And the probe
that proved state was bounded had checked four of eleven collections.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 01:10:38 +02:00
2da19a49b7 CB-WP-0021 T01/T02/T05: the engine plays its own data — AM-7 blocks
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>
2026-08-04 00:47:56 +02:00
f2281fa86c Sync hub state after adapting to ground-game's rulings
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:30:22 +02:00
custodian-sync
fe0ae1613c chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 4s
Updated by fix-consistency on 2026-08-04:
  - update .custodian-brief.md for clay-borg
2026-08-04 00:30:12 +02:00
e5a0796f2b Adapt CB-WP-0021 and CB-WP-0022 to ground-game's rulings
Some checks failed
ci / check (push) Has been cancelled
GR-S01 was ruled 2026-08-04: Surface always, union hidden priorities
1..k, k = 2/3/4. So the deal is 3/4/5 Problems, not 2/3/4; available
points 6/9/12; thresholds 5/7/9 stand; SHARED GROUND is 2-6p as printed.
The question CB-WP-0021 was going to send has been answered, and it was
the deal.

CB-WP-0021 is re-scoped. The import is now LOAD-BEARING rather than
merely correct: the ruled 6/9/12 holds only with Problems.csv values, and
the same deal with the stand-in gives 6/10/15 -- a different game that
happens to also be winnable. Fixing the deal without importing the data
would produce numbers nobody ruled on, so T05 (deal) and T02 (import)
must land together. gd0001 is to be INVERTED, not deleted: it is the
record of why this changed. gr-e01 is rewritten as a non-provisional
import check, per the ruling's own wording, and loses its provisional
owner because ground-game has now ruled.

CB-WP-0022 absorbs ground-game's process ruling, which is stricter than
this pass proposed: arithmetic findings need a runnable reproduction AND
a row-level deal table listing Surface and each hidden priority
separately, never only a sum or a deal depth. That is a direct
consequence of both premises we got wrong. So the reproduction rule gains
a SHAPE requirement, not just an existence one -- a finding that ships a
passing test but describes the wrong quantity is still a bad finding, and
that is what happened twice. T02's review brief is flipped accordingly:
press whether the rule is SUFFICIENT, not whether it is affordable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:29:45 +02:00
4e80a65742 Sync hub state for CB-WP-0023
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:21:53 +02:00
abd62c5567 CB-EV-0020: a gate moved the rule, and the report was wrong
The AM-1 coverage gate failed the build on GR-P05 being uncovered, which
is what showed the rule was in the offer layer rather than in validate.
A rule enforced only by the offer is enforced only for clients that ask
what is legal. The gate did not catch a bug, it caught a design error.

And the reported case was not the one reported. CB-WP-0018, CB-EV-0016
and the message to ground-game all described SOLVE offered on a
face-down Problem; validate already rejected face-down, so it never was.
Problem 1 is the Surface Problem, face-up from the deal, so the three
inert SOLVEs were the HAND case. The ruling covers both so nothing is
invalidated, but a ruling was requested on a wrong description -- the
second time in three passes that a premise reached ground-game
unchecked, after the '12 points available' that voided GR-E01.

Two of two. The pattern is not careless analysis; it is that a claim gets
SENT the moment it is interesting and checked afterwards. Unexecuted
verification, one step further out: not a belief acted on, but a belief
published. CB-WP-0022's reproduction rule would have caught both.

An earlier mutation run reported three survivors and was wrong -- the
replacement strings did not match, so nothing was mutated. It proved
nothing and looked like a result.

Also renames CB-WP-0022-T06B to T07; the hub flagged it as an
unregistered species.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:21:21 +02:00
custodian-sync
7a43de3f2a chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 5s
Updated by fix-consistency on 2026-08-04:
  - update .custodian-brief.md for clay-borg
2026-08-04 00:19:53 +02:00
6487d33f27 CB-WP-0023: SOLVE is legal only where it can do something
Some checks failed
ci / check (push) Failing after 4s
Implements ground-game's ruling of 2026-08-03. make all exits 0, 26
scenarios, rule coverage 59/59, and no scenario encoded the bug.

The rule ended up somewhere other than where I put it, and a gate moved
it. It went into legal_commands first; the AM-1 coverage gate then
demanded a scenario for the new GR-P05, and scenarios drive validate, not
the offer layer. A rule enforced only by the offer is enforced only for
clients that ask what is legal -- the browser would be filtered and a
scenario file would walk straight past it. Once GR-P05 moved into
validate, every condition in legal_commands was dead code, and the
layering test said so in those words.

And the reported case was not the one I reported. CB-WP-0018 and the
message to ground-game described SOLVE offered on a FACE-DOWN Problem.
Measured: validate already rejected face-down, so it never was offered.
Problem 1 is the Surface Problem, face-up from the deal -- the
maintainer's three inert SOLVEs were the HAND case, holding no Clarify
for a Clarify Problem. The ruling covers both so nothing is invalidated,
but the record was wrong.

Four conditions asserted separately, because one 'SOLVE is filtered' test
would pass with three of four implemented.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:18:40 +02:00
46c2fb652e Declare CB-WP-0023: SOLVE is offered only where it can do something
ground-game ruled SOLVE's legality on 2026-08-03: not offered on a
face-down or Denied Problem, not offered without a matching Solution in
hand, and not offered on a Problem claimed in a prior round. The bluff
reading CB-WP-0018 raised is dead -- it was a filter bug, and the engine
has been offering an inert move since legal_commands was written.

Only SOLVE is ruled on, so only SOLVE is touched. Implementing more than
was ruled would be inventing rules, which is what this exchange exists
to stop.

Chaos d8=6, no override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 23:59:23 +02:00
b513209acf GD-0001: group success is unreachable below 5 seats, with its reproduction
The maintainer played several 3-player games on 2026-08-03 and could not
win any. This says why, from the engine's own constants rather than my
arithmetic: claim every Problem the deal puts in play, concede nothing,
and the total still falls short of GR-E01's threshold at 2, 3 and 4
seats.

  2p: 2 problems worth 3  vs threshold 5 — UNREACHABLE
  3p: 3 problems worth 6  vs threshold 7 — UNREACHABLE
  4p: 3 problems worth 6  vs threshold 7 — UNREACHABLE
  5p: 4 problems worth 10 vs threshold 9 — reachable
  6p: 4 problems worth 10 vs threshold 9 — reachable

The test reads problem_priorities (GR-S01's deal) and threshold (GR-E01)
out of the engine, so it cannot drift from the rules it tests, and it
holds for either dataset -- the stand-in gives 3/6/10 and Problems.csv
gives 4/6/9 against the same 5/7/9.

It ASSERTS THE DEFECT and is expected to keep passing until ground-game
rules, then be inverted. This is CB-WP-0022's reproduction rule applied
before the register exists, because delivering feedback should not block
on building the tool that tracks it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 23:49:49 +02:00
a86efba4c3 CB-WP-0022-T01: survey — how rule systems record the ambiguity they find
CB-RES-0007 plus a runnable baseline harness. Tier L invokes the
runnable-baseline option; the external candidates are practices rather
than software, so their rows are directional and cap at parity, and the
row that CAN be run is our own.

Measured: 6 findings across 11 files with no index, 2 of 6 (33%) with a
runnable reproduction, U1-U10 raised 2026-07-30 and first READ
2026-08-03 -- 4 days, 0 of 10 ruled.

The uncomfortable number is stated before the review can find it: the
proposed 'no finding without its reproduction' rule would reject four of
our six existing findings. The survey answers rather than routes around
it -- none of the four is expensive to reproduce, so 33% is evidence
nobody was ever asked for one.

Magic corrected an assumption this pass was about to build on. Rulings
are NOT authoritative -- they are 'reminder information with no actual
weight or rules meaning' -- and the authoritative fix folds into the
Oracle card text. So a finding closes when the SOURCE changes, not when
an annotation is added, and the register must be a queue that empties
rather than an archive that grows. That is now a constraint on the ADR's
lifecycle.

Model checkers supply the reproduction rule independently: a
counterexample trace IS the finding. W3C's implementation-defined mark is
the machinery we already have in provisional: scenarios and must reuse.

The loop-lint gate caught the new tool with no --self-test; it has one,
pinning the 2-of-6 baseline so a later edit cannot move it silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 21:19:12 +02:00
bb426b3e9b Declare CB-WP-0022: the design instrument, tier L
The maintainer named a new aspect: clay-borg as a game design tool, with
a register for design flaws, questions, results and trial protocols.
Structural L on the maintainer-named-high-leverage trigger, and it amends
INTENT. Chaos d8=6, no override.

The insight is that this is already happening with no home. Five passes
have produced ten underdetermined rules points, SOLVE offered on a
face-down problem and always inert, GR-A13's wasted SOLVE, GR-E01
unreachable below 5 seats, six provisional scenario defaults, and two
scoring modes never played to the end -- every one found by BUILDING the
simulator rather than by playing it. A simulator rigorous enough to
refuse ambiguity is a design instrument, because it cannot proceed past a
rule that does not decide. All of it has been carried in prose in six
places and one sat unread in an inbox for four days.

The load-bearing rule: a design finding is not admissible without its
reproduction. A register that collects opinions would reproduce this
project's standing failure -- unexecuted verification -- in a new medium.

Recorded as a judgment for the adversarial review rather than assumed:
the engine-evolution meta the maintainer also asked about should NOT be
built, because evidence/, decisions/, gates.toml and workplans already
carry nineteen passes of it with dates, costs and falsifiers. A second
register for the same subject is ceremony. The asymmetry is the point --
engine evolution has a home and game design does not.

T05 backfills the six known findings as the TEST of the register: one
that cannot express findings the project already has is the wrong
register, and discovering that after designing it is why the order is
survey, review, decide, specify, build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:48:08 +02:00
fe3852e093 CB-WP-0021: correct the declaration before writing any code
The declaration claimed importing the edition data would resolve GR-E01.
Measured across all four scenarios: GR-S01 deals 2/3/4 problems by player
count, not all five, so 4/6/9 points are in play against thresholds of
5/7/9 -- unreachable at 2p and 3-4p with the REAL data, in the same shape
as the stand-in's 3/6/10. So GR-E01 unreachable below 5 seats is a real
property of the game and gr-e01-threshold-unreachable-2p asserts
something true.

The error was the cheap kind: 12 points exist in the file, so I assumed
12 are in play. One command over the CSV settled it and was not run until
after the declaration was committed -- this project's characteristic
error, in the pass that followed a ruling obtained because of it.

CB-EV-0018 corrected too: 'confirmed as the stand-in's doing' was
overstated. The zero came from no Problem being claimed at all.

T03 now owes ground-game a sharper question than a retirement: either
GR-S01's deal count is wrong or GR-E01's thresholds are, and no dataset
can reconcile them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:35:39 +02:00
7de0dcef87 Declare CB-WP-0021: import the edition, tier M
GROUND-WP-0002 T01 ruled the edition dataset authoritative, so the engine
must stop inventing Problem values and suits. Problems.csv carries 5
problems per scenario worth 2,2,2,3,3 (total 12) with a required_solution
each; the stand-in deals 3 worth 1,2,3 (total 6). GR-E01's thresholds of
5/7/9 are ordinary against 12 and unreachable against 6 -- which is why
the maintainer's last game ended 0 scores and winners nobody, and why
'GR-E01 unreachable below 5 seats' was carried as a rules gap. It was
never a rules gap.

Measured before declaring: AM-4a has 3,798 lines of headroom and a CSV
crate costs 21,613 marginal (csv 14,291 + csv-core 3,360 + ryu 3,962;
itoa/memchr/serde/serde_core are already present and free). 5.7x over, so
the shipped runtime cannot gain a CSV parser and that is settled by
measurement rather than preference.

The ADR's third question is the one that bites: Problem values and suits
become part of GroundState, which is hashed, so every recorded state hash
changes. A content import that quietly invalidates every hash in a
project whose central invariant is replay determinism is not a
data-loading change.

Chaos d8=7, no override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:33:00 +02:00
b8894e84b5 Sync hub state for CB-WP-0020
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:21:18 +02:00
bf24affa84 CB-WP-0020: the table you can read
Some checks failed
ci / check (push) Failing after 3s
Six of seven perceptual defects fixed; item 1 already passed.

T01, at the maintainer's instruction: a legal target restyles its
EXISTING border rather than drawing a new box. outline + outline-offset
drew a second rectangle, which an SVG viewport clips (the missing top and
left edges) and which made a seat's highlight card-sized. A border
already in the layout cannot move the layout.

T02: the ghost was a textContent copy of the card, which is why the line
break collapsed and it read as a second card, and why showing the
explanation destroyed the label. It is now a pill, the explanation is
appended beside the label, and the left-behind element is dimmed and
dashed. The stub grew innerHTML so a test can assert BOTH are present --
it could previously only see that something was displayed.

T03: NOT reproduced and recorded as not reproduced. The likeliest cause
is which element the browser reports -- for touch and pen the pointer is
captured to the pointerdown target, making every drop look like a
drop-on-itself, which is the other half of the report. elementFromPoint
is correct under both explanations. Separately the refusal was written in
element ids on the one surface a player reads when something goes wrong;
it now speaks the game's words and a test forbids id leakage.

T04: seat selections rendered as Debug. The coverage gate then failed my
first fix for dropping a field when target and problem were both set --
the aggregate does not produce that shape and the gate was right not to
care.

T05: the headline reads from group_success. 'Play again' is real, and its
first version was useless: run_game bound a fresh listener per game, so a
second game moved to a new port and left the tab pointing at a dead one.
One listener per session now, and the test asserts the second game is a
DIFFERENT deal.

Chaos d8=8 fired the first override at the new rate and drew S, changing
nothing -- one half of window 2's retirement condition.

CB-WP-0019 settled at $38.54/117 against $34.80/107. Eight for eight,
and the first under 20%.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:20:38 +02:00
5e06a7d01e Declare CB-WP-0020: the table you can read, tier S (chaos 8 override)
The perceptual check found seven items; item 1 passes. Chaos d8=8 fired
the FIRST override at the new rate, on the second roll, and drew S --
which is what the structural derivation said, so it changed nothing.
That is one half of window 2's retirement condition (retire if an
override changes nothing twice running).

The maintainer's design instruction is adopted directly: a legal drop
target should change its EXISTING border to dashed rather than draw a
new outline. That explains the hidden top/left edges (an outline on an
SVG <g> is clipped by the viewport) and the oversized seat highlight.

The ghost is a textContent copy, which is why the linebreak collapses and
it reads as a second card, and why showing the explanation destroys the
label -- one cause, two reports.

T03 carries an explicit instruction not to fix a message that already
works: the 'nothing droppable' path may simply be unreachable because
almost every part of the page is a card. Reproduce before changing.

T05 records that 0 scores and no winner is very likely the stand-in
dataset rather than a scoring bug, now that GROUND-WP-0002 T01 has ruled
the edition data authoritative. The import is its own pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:08:09 +02:00
33b373adfd Sync hub state for CB-WP-0019
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:27:01 +02:00
custodian-sync
f5bb3ae60b chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 3s
Updated by fix-consistency on 2026-08-03:
  - update .custodian-brief.md for clay-borg
2026-08-03 19:26:51 +02:00
69c1658d20 CB-WP-0019 T03/T04: the cost rule written down, and the lifecycle
Some checks failed
ci / check (push) Has been cancelled
T03: InnerLoop v1.7 plus loop-lint's own-cost check. Six passes
under-reported themselves by 30-45%, never once high, and the rule lived
only in evidence files having been re-derived three times. The READING
is load bearing, not the boundary: CB-WP-0018 T04 applied 're-run the
instrument at the moment of quoting' alone and its figure was correct.
So the operative instruction is re-run when you quote, and loop-lint
fails an evidence file naming its own workplan beside a dollar amount
without marking it provisional.

It binds forward from this pass. The check fires on seven historical
files which ARE the evidence for the rule; making them comply would edit
the record to remove the thing it proves -- the same category error as a
live fact: tag on a dated measurement, which this pass also hit.

Lifecycle, at the maintainer's instruction: ready -> active -> done,
where ready means declared and not started. loop-lint fails a workplan
that has started and still says ready, one that is active with
everything closed, and one that is done with an open task. The first
version of that check was WRONG and its own self-test caught it: it
stripped the leading status: assuming frontmatter, which silently
dropped a real task once the frontmatter said ready or active.

Both new checks then fired on this pass's own artifacts and both were
right to.

T04: CB-EV-0017. The new meta budget's first reading is a breach it
caused -- 27% against the 20% line, because this pass cost $31.18
against product passes averaging ~$21. Reported rather than exempted:
ADR-0006 D2 covers the instrument repairs but not the rule-writing, and
the honest reading is that this should have been two passes.

CB-WP-0018 settled at $36.53/95 against $28.08/82 last reported, 30%
higher. Seven for seven.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:25:18 +02:00
0b6f7c5bc8 CB-WP-0019 T01/T02: AM-4b asks what a contributor acquires
Some checks failed
ci / check (push) Failing after 4s
The two AM-4 budgets had the SAME scope -- one package, no dev edges --
while claiming to bound different things. AM-4b now measures the
workspace with dev edges: 57 crates / 725,258 lines where it read 29 /
317,021, having been blind to 28 crates and 408,237 lines, more source
than its own target.

Target 745,000, ~2.7% of room -- the same margin ADR-0008 D3 gave AM-4a,
applied to a number that grew because the instrument was repaired, not
because anything was added. The target moved to fit the measurement.

T02: proc-macros are COUNTED here and excluded from AM-4a, on purpose.
AM-4a asks what ships and a proc-macro never ships. AM-4b asks what is
acquired, and ADR-0007 D3's acquisition rule counts what the build
fetches -- 'it does not ship' is no answer to 'we downloaded it'. When
the rules disagree, the question each budget asks decides. Measured
share 109,585 lines / 15.1% against AM-4a's 36.2%, so ADR-0008 D2's
refusal to borrow the ratio was right by more than a factor of two.

Caught by this project's own earlier work twice: the mutation
find-string went stale and --self-test reported it BUILD-FREE (the check
CB-WP-0015 added after AM-4a's rotted for two passes), then the DFD gate
caught facts.toml carrying the old numbers.

CB-EV-0001 and ADR-0004 carried live fact: tags on historical readings.
A dated record asserting a CURRENT value is a category error, so those
occurrences are marked as-measured instead of retro-edited, and ADR-0004
gains a supersession note.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:04:54 +02:00
8793749654 Sync hub IDs and work-record index for CB-WP-0019
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:58:04 +02:00
custodian-sync
4e3059f578 chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 3s
Updated by fix-consistency on 2026-08-03:
  - update .custodian-brief.md for clay-borg
2026-08-03 18:57:51 +02:00
5c00c72d3b status: a cancelled task is a closed one
Some checks failed
ci / check (push) Has been cancelled
CB-WP-0005 read 5/8 and CB-WP-0007 read 2/6 in every status run since
they closed, because only 'done' counted and their remaining tasks are
'cancel'. Two permanently-wrong numbers teach the reader to skip the
column. Both now collapse into 'closed and complete' -- 18 of them --
while an open workplan with cancellations still shows the count
separately, so a cancellation is visible rather than laundered into
completion.

The workplan block is extracted as render_workplans so the control is
stated over what the tool PRINTS rather than over a literal the test
wrote: a done+cancelled fixture must collapse, and one with a real todo
must not. The first version of that control asserted arithmetic on its
own input, which is the tautological shape ADR-0010 D2 demoted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:55:11 +02:00
cc8917a6fb CB-WP-0019-T05: what the meta budget is for, at 80/20
InnerLoop v1.7. The purpose is written first and the number follows from
it: most spend on the task at hand, some on control, review and
improving the process. make status prints it above the figure, because a
threshold with no stated purpose is what let this number be argued three
times.

Soft 20% over a trailing 5, and the self-test enforces that the ratio and
the window are a PAIR: META_SOFT_PCT == 100 / TRAILING_PASSES. One meta
pass among n at parity cost reads 1/n, so 80/20 is one pass in five at
normal cost -- a five-pass window. The same 20% over three would have
silently also demanded the meta pass be half-price, which makes meta work
rushed rather than rare. Moving the ratio without the window goes red.

The phase setting is declared, argued and expiring in gates.toml, and
reverts on review_by unless re-argued. Verified live at 35%. One with no
reason or no expiry is refused rather than honoured, because a threshold
anyone may move is not a threshold.

Measured: the last five passes read 7% against the new line.

InnerLoop.md crossed the 400-line limit three times while this was
written and was fixed structurally each time -- the arithmetic, the
cost-per-response basis and the two review case studies moved to
InnerLoopReference.md. The limit was not raised.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:35:05 +02:00
07261b4444 CB-WP-0019: add T05, the meta budget's purpose and 80/20
The maintainer asked for a rule for what the budget is FOR: main spend on
the task at hand, some on control, review and improvement, 80/20 to
start, adjustable by phase.

META-25 has a threshold and no stated purpose, which is why the number
has been argued three times. The purpose goes first.

Recorded in the task: the ratio and the window are a pair. Over a
trailing 3-pass window one meta pass at parity cost is already 33%, so a
20% line there means 'one in five AND half price' rather than 'one in
five'. Over trailing 5, 20% is exactly one pass in five at normal cost,
which is the literal reading of the instruction.

The phase adjustment must be declared, argued and expiring in the shape
gates.toml already uses -- a threshold anyone may move is not a
threshold, and this project fixes limits structurally rather than
raising them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:29:41 +02:00
d8b4398096 Declare CB-WP-0019: the AM-4 family's last two defects, tier M
Both owed numbers measured BEFORE declaring, so the work is scoped
against facts. AM-4b's scope: 29 crates / 317,021 lines instrumented
against 57 / 725,258 real, so 28 crates and 408,237 lines are uncounted
-- more than its own 350,000 target. AM-4b's proc-macro share: 109,585
lines, 15.1%.

That 15.1% vindicates ADR-0008 D2, which refused to correct AM-4b using
AM-4a's measured 36.2% because 'correcting a second instrument on the
strength of the first one's ratio is the error this change exists to
fix'. Borrowing would have been wrong by more than a factor of two.

Also carries the self-quoting rule, which is six-for-six under-reported
by never less than 30% with both causes diagnosed, and still lives only
in evidence files.

Structural tier M: changes a budget's scope and target and a reporting
rule. Chaos d8=5, no override -- the first roll at the new rate.
Declaration 2 of window 2. Meta budget 0%; ADR-0006 D2 exempts
instrument repair regardless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:21:51 +02:00
c84cf37025 Sync hub IDs and work-record index for CB-WP-0018
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:24:57 +02:00
a733826d95 CB-WP-0018 T03/T04: explanations, and window 1's verdict
Some checks failed
ci / check (push) Failing after 3s
T03: input::describe writes a sentence per legal command; data-descs
carries them in step with data-targets; the ghost already following the
pointer shows the one for whatever legal target is under it, so the
explanation lands beside the target with no overlay layer to keep
aligned. ADR-0010 D1 binds -- the page renders it, never composes it.

Both mutations INITIALLY SURVIVED because the fixture's Attack card had
exactly one target, where an off-by-one shift and a truncation are both
no-ops. CB-EV-0014's lesson one level in: a fixture too thin to express
a failure is how the failure survives. Two attack targets now, both red.

T04: chaos rate d4 -> d8, window 2 open at 12 declarations, retiring if
an override changes nothing twice running. Window 1's condition was NOT
met -- both overrides changed the outcome -- so the mechanism is kept.
The weakest part of the decision is that it is a rate change argued from
n=2, so window 2 carries a falsifier: no override at all is evidence the
rate went too far, not that the mechanism is healthy.

InnerLoop.md hit 401 lines and the loadability gate fired; the rationale
moved to InnerLoopReference.md, structurally, per the standing precedent
that limits are not raised.

CB-WP-0017 settled at $9.48/40 against $5.19/23 reported mid-flight,
83% higher. Six for six, always low -- read by re-running the instrument
at the moment of quoting, which is CB-EV-0015's correction applied for
the first time.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:24:13 +02:00
7a78c58404 CB-WP-0018-T02: a game log the player can read
bot::Journal -- a shared list of Applied { actor, command, events } the
driver appends to via play_journaled; play delegates with None so
nothing existing changed. BotGame.events only appears after play
returns, which is no use to a page rendered mid-game.

Phrased with record::to_step, the recorder's vocabulary, so what the
player reads is what the scenario file will say, and all 29 GroundEvent
variants now render in words instead of Debug.

A command that produced no events says 'no effect'; the mutation
dropping that branch goes red. Honest limitation recorded: the reported
SOLVE case is resolved inside the system's resolve command, which does
produce events for other seats, so it shows as a selection with no claim
following rather than an explicit 'no effect'. Making it explicit would
mean the renderer deciding why a rule did nothing -- a second
implementation of the rules, which this task's control forbids.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:15:30 +02:00
57639623da CB-WP-0018-T01: the browser sees the end of its own game
Server::serve_end plus doc::ending, wired into both of run_game's exits.
Where the browser used to get Connection refused it now gets the ending
page with the result and the final table. It serves until the page posts
'done' (the page carries a close control), with a 600s linger so an
abandoned tab cannot hold the process open.

document() split into body() and move_section() so the ending shows the
same table rather than a second rendering of it.

The control had to be built twice and the first was worthless:
the_end_of_the_game_reaches_the_browser calls serve_end directly, and
deleting the call from run_game left it GREEN -- it tested the link and
not the chain, which is CB-EV-0012's finding recurring.
a_real_game_played_to_its_end_leaves_the_ending_on_screen runs the real
play() with a browser seat, drives a real game to its end over a real
socket, and goes red under that mutation printing an empty page -- the
reported symptom exactly.

A weak assertion of mine caught by itself: the first draft grepped the
page for location.reload, which would have forced a second script to
satisfy a test rather than a requirement. It now asserts the ending
endpoint cannot answer 'ok'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:08:04 +02:00
78e82497ee CB-WP-0018: record the card report and what it actually is
A discard pile already exists -- solution_discard on GroundState,
SolutionDiscarded removes from hand, and the page renders it. Building
one would have been building a thing that is there. What was dragged are
ACTION cards, which are not cards and are correctly never consumed;
solution cards leave the hand at Resolve because a selection is a
face-down commit.

But the report points at something real. Measured live: Investigate
draws correctly (2 -> 3 -> 4 cards), and Solve was then played three
times on problem-1 with the hand unchanged at 4 and discard empty --
because problem-1 was face down and GR-A02's resolver silently
continues. legal_commands offers Solve on every face-up problem without
consulting the hand.

Whether SOLVE should be selectable against a face-down problem or an
unmatchable suit is a game-semantics question and INTENT defers those to
ground-game. What is ours is that a provably-inert move is offered,
accepted and never accounted for. Folded into T02 as the case the log
must handle: a command that produced NO events is the one the player
needs to see.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:00:21 +02:00
e8bb7263f2 Declare CB-WP-0018: the browser is a client, tier M
Provenance: the maintainer reported 'after some time i get an empty page
back. I guess the game crashes or ends but that is unclear as the ui
disappears.' Reproduced by driving a real game to completion over HTTP:
move 5 accepted, then GET / -> Connection refused. The game ENDED
normally, 5 rounds and 30 commands, and its whole result -- coalitions,
scores, winners, hash -- went to the terminal. next_choice only accepts
connections inside a human decision point, so when play() returns the
listener dies and the post-ok reload is refused. A crash and a win
render identically: nothing. Same class as CB-WP-0016's silent drop.

Also carries the chaos rate change CB-EV-0015 owed to the next
declaration (d4 -> d8, second window of 12), which is what makes this
structurally M. Rolled at the old d4=3, no override, because a rate
changes when the decision lands and not retroactively.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 22:59:05 +02:00
332d8f1bba Sync hub IDs and work-record index for CB-WP-0017
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 22:43:40 +02:00
a55e878bf0 CB-WP-0017: legible interaction, and the chaos window's verdict
Some checks failed
ci / check (push) Failing after 4s
Provenance (tier M, structural S, chaos d4=4 -> OVERRIDE drawn M):
the maintainer could drag after CB-WP-0016 but could not tell what was
pickable, held, or droppable. Underneath that, the page was WRONG about
which moves exist: 9 legal commands rendered as 5 cards each claiming
all three target kinds, from a const string in the emitter. Investigate
is legal on problems 2 and 3 but not 1; Solve on 1 but not 2 or 3. The
live page now says 'Solve onto problem 1'.

ADR-0010 restates control 5, which this work would otherwise have
outgrown in silence: every game fact the page acts on must arrive from
Rust as data; the script may read, match and render it, never compute,
infer, filter or default one. The survey's real finding is that the
permitted and forbidden designs are indistinguishable from outside, so
the vocabulary grep is demoted to a cheap first line and two behavioural
properties become the controls -- the highlighted set EQUALS the set
Rust emitted, and anything the page marks legal must resolve. Both
mutation-proven; the derive-legality mutation produces a plausible
highlight (seat-0,1,2 where only seat-1 is legal) and is caught.

Visible now: .pick resting shadow, .held on the grabbed element, .dropok
on every legal target including BOTH drawings of a seat, and a ghost
following the pointer. Nothing perceptual is verified and ADR-0010 D5
says so.

The DOM stub now models classList/querySelectorAll/createElement and
builds its node set from the real emitted page. Trap recorded: QuickJS
fixes its stack limit at Context creation relative to that frame, so a
helper returning a Context makes every later eval report
'SyntaxError: stack overflow'.

CHAOS WINDOW CLOSED, 12 declarations, 2 overrides, one each way. Both
changed the outcome, so the retirement condition is not met. Verdict:
keep, and recommend d4 -> d8 with a second window of 12 -- that is a
change to the loop's own constraints and is owed to the next declaration
as tier-M work, not made here.

CB-EV-0014 corrected: it quoted CB-WP-0015 at $15.14/136 and called it
the first settled figure quoted. Now $22.70/166. The number had been
read during CB-WP-0015 itself, so there are two defects -- the boundary,
and quoting from memory instead of re-running the instrument.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 22:42:45 +02:00
ca92db53bc Declare CB-WP-0017: legible interaction, tier M (structural S, chaos 4)
Provenance: the maintainer ran stage 1's check after CB-WP-0016, could
drag, and reported that the UI gives no way to tell what can be picked
up, what is being dragged, or where it may be dropped. Combined with the
prior finding that the page hides which moves are legal -- measured, 9
legal commands rendered as 5 cards each claiming all three target kinds,
with Investigate legal on problems 2 and 3 but not 1 -- highlighting drop
targets is not decoration, it is the first time the page tells the truth.

Structural tier S: presentation work inside an existing capability.
CHAOS ROLLED 4 -> OVERRIDE, drawn tier M. Second override in twelve
declarations and it rolls the opposite way from the first (CB-WP-0011 was
L rolled down to S), so the calibration window closes with one of each,
which is the minimum that makes its evaluation possible.

Declaration 12 of 12 -- the window closes here and T04 owes the verdict.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 22:32:50 +02:00
b87dbec55b Sync hub IDs and work-record index for CB-WP-0016
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 20:49:21 +02:00
bf72a1863a CB-WP-0016: the drop target that was never there
Some checks failed
ci / check (push) Failing after 4s
Provenance (tier S, one paragraph in lieu of survey and ADR): the human
check that kept INTENT stage 1 open was run and the drag was broken.
Root cause, worth more than the instance: drop targets were ids, and an
id must be unique, so exactly one element could ever be seat-0. The
relationship-graph circle took it and the seat card that every action
card's own text points at -- 'drag Attack onto a seat' -- silently had
none. A seat is drawn twice and both drawings are the seat; the document
model could not express that.

Drop keys are now data-drop. Any number of elements may carry the same
key, so a seat is droppable on its card and on its graph node. Measured
on a live server: seat-0/1/2 each appear twice, id survives only on
cb-status which is the one element the script looks up, and
down=action-attack&up=seat-1 returns ok.

Second defect: a drop on nothing returned without posting and without
touching the status line, so a broken target was indistinguishable from
a working page. resolve already refuses rather than defaulting, which is
right; refusing SILENTLY is not. The page now reports the raw fact --
'took action-attack, let go over nothing droppable' -- which names
elements, not moves, so ADR-0007 control 5 holds.

And the honest part: the general check added here -- every offered
affordance names a key that exists, driven through Policy::choose over
four real bot games -- does NOT catch the reported defect. seat-0 did
exist, on the graph circle. It is kept because a wholly absent target is
a real class, and paired with a targeted regression test that does catch
it. Three mutations, each red for its stated reason, including the
reported defect reintroduced; only the targeted test fires on that one.

A cb-play assertion matched id="action-ground" as a substring while
describing itself as checking the page; rewritten through drop_keys.

make all exits 0. Stage 1 stays open: verified by tests, mutation and a
live server, not by a human dragging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 20:48:18 +02:00
4df2d0a4ae Declare CB-WP-0016: the drop target that was never there, tier S
Provenance (tier S, one paragraph in lieu of survey and ADR): the human
check CB-EV-0012 kept stage 1 open on was run by the maintainer and
found the drag broken. Diagnosed against the live server first:
down=action-attack&up=seat-1 returns ok, so socket, guard, resolve and
dispatch are correct. seat-{n} ids exist only on the SVG circles in the
relationship graph; player_card emits the visible seat cards with no id,
so the target every action card names is inert. jsrun feeds element ids
straight in and never hit-tests, which is why every test passed.
Structural tier S: a defect fix inside an existing capability, and the
check it adds is a product test rather than a control gate. Chaos d4=3,
no override. Declaration 11 of 12.

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