Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
| id | kind | title | status | state_hub_workstream_id |
|---|---|---|---|---|
| CB-WP-0017 | product | Legible interaction: what can be picked up, what is being dragged, where it can go | done | e5080127-8cde-40de-bf03-2898a98491ca |
Purpose
structural tier S (presentation work inside an existing capability —
no new port, no canonical interface, no new
dependency, no change to the loop's own constraints)
chaos d4 = 4 → OVERRIDE, tier drawn: M
declared tier M (structural S, chaos 4 → M)
Declaration 12 of 12 — the chaos calibration window closes here, and owes its evaluation (T04).
This is the second override in twelve declarations, and it rolls the opposite way from the first: CB-WP-0011 was structural L rolled down to S, this is structural S rolled up to M. The window therefore has one of each to evaluate, which is the minimum that makes an evaluation possible at all.
Tier M means survey and decision merge into one document and the adversarial review is optional.
The report this pass exists for
The maintainer ran stage 1's human check again after CB-WP-0016 and could drag — but:
"it is not possible to get what moves are possible and how they affect the state of the game" … "Can i have better visualization of which Elements in the ui can be manipulated" … "If the element isnt shown as picked up, there is no way to understand that i am actually dragging it or where i am dragging it" … "visual clues about where something can be dropped when it is picked up"
Four requests, and one prior finding that makes them one pass: the page already hides which moves are legal.
Measured, seat P1 at round 1 step Select — 9 legal commands, rendered as 5 cards each claiming all three target kinds:
| the card says | actually legal |
|---|---|
| Investigate → "a seat, a problem, or the table" | problem-2, problem-3 |
| Solve → "a seat, a problem, or the table" | problem-1 |
| Attack → "a seat, a problem, or the table" | seat-1, seat-2 |
| Support → "a seat, a problem, or the table" | seat-1, seat-2 |
| Ground → "a seat, a problem, or the table" | table |
Investigate is legal on problems 2 and 3 but not 1; Solve on 1 but not 2
or 3. The card text is a constant — "drag {a:?} onto a seat, a problem, or the table" — emitted whenever any legal command uses that
action, then naming all three kinds regardless. legal carries the exact
targets and the renderer discards them.
So highlighting drop targets is not decoration on top of a working table. It is the first time the page will tell the truth about what is legal.
Task: decide how far the JavaScript may grow
id: CB-WP-0017-T01
status: done
priority: high
state_hub_task_id: "be0f8249-51b2-4fef-89c0-9fad660563b4"
Write decisions/ADR-0010-*.md (tier M: survey and decision in one).
This is a real decision, not a formality. ADR-0007 Decision 5 says the
page "reports raw pointer facts and nothing else, and Rust decides what
command they mean", and doc.rs says of SCRIPT: "it does one thing".
Everything asked for here needs the script to do more than one thing —
track a drag in flight, move a ghost with the pointer, add and remove
classes on other elements.
Decide, explicitly:
- Does class-toggling violate control 5? The honest argument is that legality still comes from Rust as data and the script only renders it — but that argument must be made, because "the page renders what Rust said" is exactly what a page constructing commands would also claim.
- What is the invariant that replaces "it does one thing"? If the script may grow, say what it may never do, in a form a test can check. The existing no-game-vocabulary test is the candidate and it is weak.
- The cost. Marginal AM-4a is zero either way, so the budget does not decide this. Say what does.
Done 2026-08-02. ADR-0010 — the script may render state it was given; it may not derive any.
The survey's real output: 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". So the vocabulary grep is demoted to a cheap first line and the load-bearing controls are stated over behaviour: the highlighted set must equal the set Rust emitted, and anything the page marks legal must resolve. Both are mutation-proven below.
Task: tell the truth about which moves are legal
id: CB-WP-0017-T02
status: done
priority: high
state_hub_task_id: "749e03f3-70b6-458d-be9c-4a178808af9e"
Emit the legal targets derived from legal, per action, and render
them so the table is legible without dragging anything.
The generic sentence goes. A card that says "onto a seat, a problem, or
the table" when only problem-1 is legal is not a simplification, it is
wrong.
Controls:
- The emitted target set must equal the affordance targets of the legal list exactly — pure Rust, no browser, and it must go red when the sets diverge.
- A drop that the page marked legal must resolve. If the page can
advertise a target
resolvethen refuses, the two have drifted and the highlighting is worse than none.
Done 2026-08-02. From the live server: "Solve onto problem 1", "Investigate onto problem 2, problem 3", "Attack onto P2, P3", "Ground onto the table". The constant sentence is gone.
everything_the_page_advertises_actually_resolves drives four real bot
games and checks every emitted target against resolve itself. Mutation —
adding table to Investigate's targets — goes red with "the page
advertises action-investigate -> table but resolve refuses it".
Task: make picking up, dragging, and dropping visible
id: CB-WP-0017-T03
status: done
priority: high
state_hub_task_id: "e607e4e1-3270-458d-9c49-be77d48cff0a"
The four requests, in order of how badly they are needed:
- What can be manipulated at all — a resting affordance on every pickable element. The maintainer asked for a slight shadow; the point is that interactive and inert must not look identical.
- What is picked up — the grabbed element must visibly change while the pointer is down.
- Where it can go — every legal drop target for the thing currently held highlights, and nothing else does.
- Where it is going — the drag must be followable, not invisible.
Be honest about what is testable. Whether a shadow reads as "you can pick this up" is not something any test here can settle, and claiming otherwise would be this project's characteristic error in its newest costume. What is testable, and must be:
- pointerdown marks the grabbed element and exactly the legal targets for it, and nothing else;
- pointerup and cancellation clear every mark — a drag that ends leaves no residue, or the next drag highlights a stale set;
- the highlighted set is the same set T02 emitted.
The JS DOM stub cannot express any of this today. It will have to model
classList and element lookup, which is a real deepening of the harness —
and CB-WP-0016 showed that a stub too thin to express a failure is how the
failure survives.
Done 2026-08-02. .pick (resting shadow, lift on hover), .held
(dimmed and scaled), .dropok (dashed outline on every legal target), and
a ghost following the pointer.
The stub now models classList, querySelectorAll, createElement and a
body, and its node set is built from the real emitted page via
droppables(html) rather than from a list the harness invented.
Three mutations, each red:
| mutation | result |
|---|---|
the script derives targets from the DOM instead of reading data-targets |
red — marks seat-0, seat-1, seat-2 where only seat-1 is legal. A plausible-looking highlight, caught. This is ADR-0010 D1 enforced |
the page advertises a target resolve refuses |
red |
pointercancel and the clear() on pointerup are dropped |
red — "marks survived __up('seat-1')" |
A seat highlights on both of its drawings — card and graph node — and
that is asserted rather than tolerated. The first draft of the assertion
was wrong about it: it expected seat-1 and read seat-1,seat-1.
A real trap, recorded: QuickJS fixes its stack limit at Context
creation relative to the frame it was created in, so a helper that creates
a context and returns it makes every later eval report SyntaxError: stack overflow — for __all.length. The caller must own the Context.
Nothing perceptual is verified. Whether a shadow reads as "pick me up" is not reachable from here (ADR-0010 D5).
Task: evidence, and close the chaos calibration window
id: CB-WP-0017-T04
status: done
priority: high
state_hub_task_id: "b67442a5-ad27-495c-857a-b4ebe28b07bf"
evidence/CB-EV-0015-*.md, and then the evaluation the window has owed
since 2026-07-31.
gates.toml states the retirement condition in its own words:
retire_if = "the window closes with no overridden tier producing a different outcome than the argued one — the evaluation this window exists to make possible"
So answer it, on the two overrides actually observed:
- CB-WP-0011, structural L → S. What did the deleted survey cost or save? CB-EV-0009 §1 and CB-EV-0010 §5 already priced part of this (0.123 $/response at L against 0.099 at S on the same subject).
- CB-WP-0017, structural S → M. What did the extra ADR buy that a tier-S provenance paragraph would not have? If the answer is nothing, say so — that is the finding the window was opened to produce, and a mechanism that survives its own evaluation by being graded generously is worse than one that is retired.
Then: keep, retire, or change the rate, with the argument.
Also due:
- Whether stage 1 closes. It needs a human to drag again. Say so.
- Quote CB-WP-0016's cost, and apply CB-EV-0013 §5's correction — quote a boundary that has actually settled, or say it has not.
Done 2026-08-02.
CB-EV-0015. make all
exits 0.
- The chaos window's verdict: keep, at a lower rate. Both overrides changed the outcome, so the retirement condition is not met. CB-WP-0011 (L→S) bought a defect in the existing renderer that a toolkit survey would have walked past, at 0.099 $/response against 0.123. CB-WP-0017 (S→M) bought the restatement of control 5 — at tier S the script would have outgrown ADR-0007 D5 under a one-paragraph commit note. Recommendation: d4 → d8, second window of 12, retire if an override changes nothing twice running. That is a change to how the loop constrains its own operation, so it is tier-M work this pass does not do — it is owed to the next declaration.
- CB-EV-0014 is corrected. It quoted CB-WP-0015 at $15.14/136 and claimed that was the first settled figure this project had quoted. Both wrong: it now reads $22.70/166. The number had been read during CB-WP-0015 itself and carried forward, so there are two defects — the boundary, and quoting a figure from memory rather than re-running the instrument. Five for five, always low.
- Stage 1 stays open, and now on a purely perceptual question.