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>
6.6 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 | 136 | $15.14 | 0.111 |
| CB-WP-0016 | product | provisional — not quoted |
CB-EV-0013 §5 found the self-quoting rule fixes the wrong boundary: a pass's window runs to the next pass's first commit, so CB-WP-0015's figure was still open when quoted. It is quoted here after CB-WP-0016's declaration commit closed it, which is the first figure this project has quoted at a boundary that had actually settled.
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.