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>
7.1 KiB
CB-EV-0014 — what the human check bought
CB-WP-0016 T03. Measured 2026-08-02 at 4df2d0a+. Pass kind product,
tier S (chaos d4=3, no override). Declaration 11 of 12.
Cost quotes CB-WP-0015's figure, per CB-EV-0012's rule and with the correction CB-EV-0013 §5 attached to it. See §5.
1. The check found a defect every test in the repo was blind to
CB-EV-0012 §4 kept INTENT stage 1 open on one action the loop could not perform. The maintainer ran it. The table reads. The drag did not work.
Diagnosed against the live server before any code changed, which is what made the rest cheap:
POST down=action-attack&up=seat-1 → ok (the game advanced)
POST down=action-attack&up=action-attack → "not a legal move here"
Socket, token guard, resolve and dispatch: all correct. The defect was in
the page, and it had a root cause worth more than the instance:
Drop targets were
ids, and anidmust be unique. So exactly one element could ever beseat-0. The relationship-graph circle took it, and the seat card — which 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.
2. Why nothing caught it, stated precisely
| control | why it was blind |
|---|---|
jsrun::gesture |
calls __down(id), which synthesized {target:{id}}. It feeds element ids straight in and never hit-tests — it establishes "the script posts the ids it was given", never "there is an element there to give" |
| the 42-path coverage gate | asserts each view field is present in the parsed document. A <div> with no id satisfies that perfectly |
resolve's unit tests |
test the mapping from a fact to a command. The fact never arrives |
| M-D1-MUT | its population is the AM-* acceptance rows. None of them is about the page |
Every one was green. This is the shape CB-WP-0015 closed one layer in — a harness answering a narrower question than its name implies — recurring in the presentation layer.
3. The fix, and the check that is honestly insufficient
Drop keys are now data-drop, not id. Any number of elements may
carry the same key, so a seat is droppable on its card and on its graph
node. Measured on the live page: seat-0, seat-1, seat-2 each appear
twice; id survives on exactly one element, cb-status, which is the
only one the script looks up.
Two checks, and the difference between them is the finding:
| check | catches the reported defect? |
|---|---|
every offered affordance names a key that exists — drives four real bot games through Policy::choose, renders at every real decision point |
NO. seat-0 did exist, on the graph circle |
| every seat card is a drop target, not only the graph node | yes |
The general check is worth having — it fails when a target is wholly absent, which is a real class — but it would not have found the bug the maintainer found, and saying otherwise would be the exact error this project keeps catching. An existence check over a whole document cannot tell you the element the user is being pointed at is the one that works.
Hooking Policy::choose rather than re-driving the game by hand matters:
those are the same decision points cb-play --serve renders at, with the
same legal list. A hand-rolled walk would be a second implementation of
the loop, free to agree with itself while disagreeing with what ships.
Mutations, each red for its stated reason
| mutation | result |
|---|---|
| the seat card loses its drop key — the reported defect, reintroduced | red, and only the targeted test fired; the general one stayed green |
| the table stops being a drop target | red — "whose DROP id table is not an element in the document" |
the silent return comes back |
red — "a drop on nothing said """ |
4. Silence was the second defect
SCRIPT did if (!down || !up) { down = null; return; } — no POST, no
status line, nothing at all. That is why a human sitting in front of it
could not tell a broken target from a working page.
The Rust side already held the right principle: resolve refuses rather
than substituting a default, because "a drag that means nothing must mean
nothing, not the first legal move." Refusing is right. Refusing
silently is not, and the two had been conflated.
The page now reports the raw fact — "took action-attack, let go over nothing droppable". It names elements, not moves, so ADR-0007 control 5 is intact and the body-shape assertion still holds.
5. What the human check cost, and what it bought
Stage 1 was held open on this check for two passes (CB-WP-0014, CB-WP-0015), against a standing temptation to close it on green tests — CB-EV-0012 §4 recorded that temptation explicitly and refused it.
It bought a defect that made the stage's headline interaction non-functional on its primary target, plus a root cause in the document model, plus a control class that did not exist. Two passes of delay was the right price, and the reasoning that kept it open — "no test in this repo can reach it" — was exactly correct rather than merely cautious.
The stage does not close here either. The fix is verified by tests, by mutation, and against a live server; it is not verified by a human dragging. That is the same standard that found this, and the same one that would have missed it.
| pass | kind | responses | cost | $/response |
|---|---|---|---|---|
| CB-WP-0015 | product | |||
| CB-WP-0016 | product | provisional — not quoted |
CORRECTED 2026-08-02 (CB-EV-0015 §6). This file originally reported
CB-WP-0015 at $15.14 / 136 and claimed that figure was "the first
this project has quoted at a boundary that had actually settled." The
claim was false and the number was wrong. $15.14/136 was read from
make status during CB-WP-0015 itself and carried forward — it was that
pass's own in-flight figure, which is precisely what CB-EV-0012's rule
exists to forbid. Measured after CB-WP-0017's declaration closed the
window: $22.70 / 166, higher by 50%.
So the defect is not only the boundary CB-EV-0013 §5 identified. It is that a figure gets read once and quoted later, and a number read earlier in a session is an in-flight number no matter which pass's name is on it.
6. Open
- INTENT stage 1: still one human verification, now of a fix rather
than of an unknown.
cb-play --serve 0, drag an action onto a seat card. - The self-quoting rule still names the wrong boundary — quote two passes back. Owed since CB-EV-0013 §5.
- AM-4b's scope defect (408,237 uncounted lines) and its unmeasured proc-macro share.
python3as a toolchain dependency was never argued.- AM-4a cannot survive stage 2 — 1,741,979 against 161,000.
- ADR-0007 D3's acquisition rule remains unratified after deciding two dependency questions.
- Chaos: 11 of 12 declarations, 1 override. The calibration window closes on the next declaration and owes an evaluation.