222 lines
10 KiB
Markdown
222 lines
10 KiB
Markdown
|
|
# CB-EV-0015 — legible interaction, and the chaos window's verdict
|
||
|
|
|
||
|
|
CB-WP-0017 T04. Measured 2026-08-02 at `ca92db5`+. Pass kind `product`,
|
||
|
|
tier **M** (structural S, **chaos d4=4 → OVERRIDE, drawn M**).
|
||
|
|
**Declaration 12 of 12 — the chaos calibration window closes here.**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. The page was lying, and now it is not
|
||
|
|
|
||
|
|
The maintainer could drag after CB-WP-0016 but could not tell what was
|
||
|
|
draggable, what was held, where it could go, or what any of it would do.
|
||
|
|
Underneath that was something worse than a missing affordance: the page
|
||
|
|
was **wrong about which moves exist**.
|
||
|
|
|
||
|
|
Seat P1, round 1, step Select — **9 legal commands**, rendered as 5 cards
|
||
|
|
each claiming all three target kinds. Before and after, from the live
|
||
|
|
server:
|
||
|
|
|
||
|
|
| card | before | after |
|
||
|
|
|---|---|---|
|
||
|
|
| Investigate | *"onto a seat, a problem, or the table"* | **onto problem 2, problem 3** |
|
||
|
|
| Solve | *"onto a seat, a problem, or the table"* | **onto problem 1** |
|
||
|
|
| Attack | *"onto a seat, a problem, or the table"* | **onto P2, P3** |
|
||
|
|
| Support | *"onto a seat, a problem, or the table"* | **onto P2, P3** |
|
||
|
|
| Ground | *"onto a seat, a problem, or the table"* | **onto the table** |
|
||
|
|
|
||
|
|
Investigate is legal on problems 2 and 3 but **not** 1; Solve on 1 but not
|
||
|
|
2 or 3. The old text was a `const` in the emitter, printed whenever *any*
|
||
|
|
legal command used that action. `legal` carried the exact targets and the
|
||
|
|
renderer threw them away.
|
||
|
|
|
||
|
|
So drop-target highlighting is not decoration on a working table. It is
|
||
|
|
the first time the page tells the truth about what is legal.
|
||
|
|
|
||
|
|
## 2. ADR-0010: the constraint that replaced "it does one thing"
|
||
|
|
|
||
|
|
ADR-0007 D5 said the page *"reports raw pointer facts and nothing else"*
|
||
|
|
and `doc.rs` said `SCRIPT` *"does one thing"*. Everything asked for here
|
||
|
|
needs it to do more — hold a drag, follow the pointer, mark other
|
||
|
|
elements. Restating a control in order to permit what it forbade is how
|
||
|
|
controls die, so it was restated out loud:
|
||
|
|
|
||
|
|
> **Every game fact the page acts on must arrive from Rust as data.** The
|
||
|
|
> script may read it, match it, and turn it into presentation. It may not
|
||
|
|
> compute, infer, filter, or default one.
|
||
|
|
|
||
|
|
The survey's real finding: the permitted design (Rust emits legality, the
|
||
|
|
script matches it) and the forbidden one (the script derives legality from
|
||
|
|
the DOM) are **indistinguishable from the outside** — both are "the page
|
||
|
|
highlights some elements". A control that cannot separate them is not a
|
||
|
|
control.
|
||
|
|
|
||
|
|
The old control was a grep of `SCRIPT` for game vocabulary. CB-WP-0014
|
||
|
|
already found that shape too weak once. It is now demoted to a cheap first
|
||
|
|
line, and the load-bearing control is stated over behaviour:
|
||
|
|
|
||
|
|
**Property 1 — the highlighted set equals the set Rust emitted.** The
|
||
|
|
mutation that makes the script derive targets by pattern-matching ids
|
||
|
|
produces a *plausible* highlight — `seat-0, seat-1, seat-2` where only
|
||
|
|
`seat-1` is legal — and the test catches it. That is Decision 1 actually
|
||
|
|
enforced rather than asserted.
|
||
|
|
|
||
|
|
**Property 2 — a target the page marks legal must `resolve`.** A page that
|
||
|
|
advertises a drop Rust then refuses is worse than no highlighting, because
|
||
|
|
it teaches the player something false. Mutation: adding `table` to
|
||
|
|
Investigate's targets → *"the page advertises action-investigate -> table
|
||
|
|
but resolve refuses it"*.
|
||
|
|
|
||
|
|
## 3. What is visible now, and what is merely tested
|
||
|
|
|
||
|
|
| request | done | verified how |
|
||
|
|
|---|---|---|
|
||
|
|
| what can be picked up | `.pick` — shadow, lift on hover, `grab` cursor | **not verified.** Whether it *reads* as pickable is a human check |
|
||
|
|
| what is held | `.held` — dimmed and scaled where it sat | JS test: `__heldKeys()` is exactly the grabbed key |
|
||
|
|
| where it can go | `.dropok` on every legal target, **both drawings of a seat** | JS test: set equality with `data-targets` |
|
||
|
|
| where it is going | a ghost following the pointer | JS test: ghost exists, and its `left` tracks `clientX` |
|
||
|
|
| what moves exist | the card names its real targets | live server, §1 |
|
||
|
|
|
||
|
|
**A seat is marked on both of its drawings** — card and graph node. That
|
||
|
|
is asserted rather than tolerated: highlighting only one would send the
|
||
|
|
player back to hunting for the one that works, which is the CB-WP-0016
|
||
|
|
defect in a new form. The first draft of that assertion was *wrong* about
|
||
|
|
it, expecting `seat-1` and reading `seat-1,seat-1`.
|
||
|
|
|
||
|
|
**Third mutation:** dropping the `pointercancel` handler and the `clear()`
|
||
|
|
on pointerup leaves marks behind — *"marks survived `__up('seat-1')`"*. A
|
||
|
|
stale highlight set is worse than none.
|
||
|
|
|
||
|
|
### What this pass does not claim
|
||
|
|
|
||
|
|
No visual property is verified. Whether the shadow reads as *"pick me
|
||
|
|
up"*, whether the highlight is noticeable, whether the drag feels
|
||
|
|
followable — none of it is reachable from here, and ADR-0010 D5 says so
|
||
|
|
rather than letting the tests imply otherwise.
|
||
|
|
|
||
|
|
## 4. The harness had to get deeper, and cost an hour to a real trap
|
||
|
|
|
||
|
|
The DOM stub could not express any of this: it synthesized
|
||
|
|
`{target:{id}}` from whatever key the test passed. It now models
|
||
|
|
`classList`, `querySelectorAll`, `createElement`, and a body — **and its
|
||
|
|
node set is built from the real emitted page** via `droppables(html)`,
|
||
|
|
not from a list the harness invented.
|
||
|
|
|
||
|
|
**The trap, recorded because it will recur:** QuickJS fixes its stack
|
||
|
|
limit at `Context` creation *relative to the frame it was created in*. A
|
||
|
|
helper that creates a context and returns it leaves every later `eval`
|
||
|
|
running in a shallower frame, the comparison underflows, and QuickJS
|
||
|
|
reports `SyntaxError: stack overflow` — for `__all.length`. The fix is
|
||
|
|
that the caller owns the `Context` and the helper only prepares it.
|
||
|
|
|
||
|
|
That is also why one test had silently skipped registration: each test
|
||
|
|
hand-rolled its setup. `prepare()` is now shared, so a test cannot set up
|
||
|
|
a different page than the harness runs.
|
||
|
|
|
||
|
|
## 5. The chaos calibration window: the verdict it has owed
|
||
|
|
|
||
|
|
`gates.toml` states the retirement condition in its own words:
|
||
|
|
|
||
|
|
> *"the window closes with no overridden tier producing a different outcome
|
||
|
|
> than the argued one — the evaluation this window exists to make
|
||
|
|
> possible"*
|
||
|
|
|
||
|
|
Twelve declarations, **two overrides**, one in each direction. That is the
|
||
|
|
minimum that makes an evaluation possible at all, and it arrived on the
|
||
|
|
last declaration.
|
||
|
|
|
||
|
|
### Override 1 — CB-WP-0011, structural L → S
|
||
|
|
|
||
|
|
The deleted survey would have opened on 2D toolkits. What the pass found
|
||
|
|
instead, at tier S, was that the **existing** text renderer was showing 24
|
||
|
|
of 41 view fields (CB-EV-0009 §1) — a defect a toolkit survey would have
|
||
|
|
walked straight past. Priced on the same subject: **0.099 $/response at S
|
||
|
|
against 0.123 at L** (CB-EV-0010 §5).
|
||
|
|
|
||
|
|
**Different outcome than the argued one: yes.** The argued tier would have
|
||
|
|
bought a survey of alternatives; the rolled tier bought a defect in what
|
||
|
|
already existed.
|
||
|
|
|
||
|
|
### Override 2 — CB-WP-0017, structural S → M
|
||
|
|
|
||
|
|
The question the retirement condition asks: **what did the extra ADR buy
|
||
|
|
that a tier-S provenance paragraph would not have?**
|
||
|
|
|
||
|
|
It bought the restatement of control 5. At tier S this pass would have
|
||
|
|
widened what the JavaScript does — pointermove, class-toggling on other
|
||
|
|
elements, a ghost node — under a one-paragraph commit note, and ADR-0007
|
||
|
|
D5's *"reports raw pointer facts and nothing else"* would have been
|
||
|
|
silently outgrown rather than replaced. The forced document is what
|
||
|
|
produced Decision 1's rule and, more importantly, the finding that the
|
||
|
|
permitted and forbidden designs are indistinguishable from outside — which
|
||
|
|
is what turned the vocabulary grep from *the* control into a cheap first
|
||
|
|
line, and produced the two properties that are now the real controls.
|
||
|
|
|
||
|
|
**Different outcome than the argued one: yes**, and in the direction that
|
||
|
|
is harder to see from inside — the extra process caught a control quietly
|
||
|
|
expiring.
|
||
|
|
|
||
|
|
### Verdict: keep, at a lower rate
|
||
|
|
|
||
|
|
Both overrides changed the outcome, so the retirement condition is not
|
||
|
|
met. But the honest reading of the cost is that d4 fired twice in twelve
|
||
|
|
and both were informative *because they were rare*; a mechanism that
|
||
|
|
overrides a quarter of all declarations stops being a calibration and
|
||
|
|
starts being the tier system.
|
||
|
|
|
||
|
|
**Recommendation: keep the chaos roll, drop the rate from d4 to d8, and
|
||
|
|
open a second window of 12** with a new retirement condition — retire if
|
||
|
|
an override changes nothing twice running. This is a change to how the
|
||
|
|
loop constrains its own operation and is therefore **tier M work that this
|
||
|
|
pass does not do**: it is recorded here as the recommendation and owed to
|
||
|
|
the next declaration, which must make it with its own tier and its own
|
||
|
|
roll.
|
||
|
|
|
||
|
|
## 6. Cost
|
||
|
|
|
||
|
|
| pass | kind | responses | cost | $/response |
|
||
|
|
|---|---|---|---|---|
|
||
|
|
| **CB-WP-0016** | product | 64 | **$14.93** | 0.233 |
|
||
|
|
| CB-WP-0017 | product | *provisional — not quoted* | | |
|
||
|
|
|
||
|
|
Read from `make status` **at the moment of writing**, after CB-WP-0017's
|
||
|
|
declaration closed CB-WP-0016's window.
|
||
|
|
|
||
|
|
### The self-quoting rule failed a second way, and CB-EV-0014 is corrected
|
||
|
|
|
||
|
|
CB-EV-0014 §5 quoted CB-WP-0015 at **$15.14 / 136** and claimed it was
|
||
|
|
*"the first figure this project has quoted at a boundary that had actually
|
||
|
|
settled."* Both parts were wrong. The same tool now reports CB-WP-0015 at
|
||
|
|
**$22.70 / 166** — higher by 50%. CB-EV-0014 has been corrected in place.
|
||
|
|
|
||
|
|
$15.14/136 was read during CB-WP-0015 itself and carried forward into the
|
||
|
|
next pass's write-up. So there are **two** defects, not one:
|
||
|
|
|
||
|
|
1. the boundary is wrong — a window runs to the *next* pass's first commit
|
||
|
|
(CB-EV-0013 §5);
|
||
|
|
2. **a figure read earlier in a session is an in-flight figure regardless
|
||
|
|
of whose name is on it.** Quoting from memory defeats the rule even
|
||
|
|
when the boundary is right.
|
||
|
|
|
||
|
|
Five for five now, always low: every self-reported cost in this project
|
||
|
|
has been an underestimate. The rule owed is therefore not just *"quote two
|
||
|
|
passes back"* but *"re-run the instrument at the moment you quote it"* —
|
||
|
|
and the second half is what this file did.
|
||
|
|
|
||
|
|
**Meta budget: 0% `[ok]`** over the trailing three, all product.
|
||
|
|
|
||
|
|
## 7. Open
|
||
|
|
|
||
|
|
- **INTENT stage 1: the human check, again.** Everything perceptual here
|
||
|
|
is unverified by construction. `cb-play --serve 0`: can you see what is
|
||
|
|
pickable, what you are holding, and where it may go?
|
||
|
|
- **Chaos: the d4→d8 recommendation** and a second window. §5.
|
||
|
|
- **The self-quoting rule still names the wrong boundary.** CB-EV-0013 §5.
|
||
|
|
- **AM-4b's scope defect (408,237 uncounted lines)** and its proc-macro
|
||
|
|
share.
|
||
|
|
- **`python3` as a toolchain dependency was never argued.**
|
||
|
|
- **AM-4a cannot survive stage 2** — 1,741,979 against 161,000.
|
||
|
|
- **ADR-0007 D3's acquisition rule** unratified after deciding two
|
||
|
|
dependency questions; **ADR-0010** now rests on D5, which is also
|
||
|
|
unratified.
|