From 4df2d0a4ae3fa054a539a25c28758a346a8edc25 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 2 Aug 2026 20:35:18 +0200 Subject: [PATCH] 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 --- workplans/CB-WP-0016-the-drop-target.md | 136 ++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 workplans/CB-WP-0016-the-drop-target.md diff --git a/workplans/CB-WP-0016-the-drop-target.md b/workplans/CB-WP-0016-the-drop-target.md new file mode 100644 index 0000000..f55dc90 --- /dev/null +++ b/workplans/CB-WP-0016-the-drop-target.md @@ -0,0 +1,136 @@ +--- +id: CB-WP-0016 +kind: product +title: "The drop target that was never there" +status: todo +--- + +# Purpose + +``` +structural tier S (a defect fix inside an existing capability — no new + port, no canonical interface, no new dependency, and + the check it adds is a product test, not a control + gate) +chaos d4 = 3 → no override +declared tier S +``` + +Declaration 11 of 12. Tier S: one provenance paragraph in the commit +message, no survey and no ADR. Steps 4–5 are not skipped. + +## Found by the human check, and by nothing else + +CB-EV-0012 §4 kept INTENT stage 1 open on one action the loop could not +perform: run `cb-play --serve 0`, open the URL, confirm the table reads and +a drag works. The maintainer ran it on 2026-08-02. **The table reads. The +drag does not.** + +Diagnosed against the running server before any code was changed: + +``` +POST down=action-attack&up=seat-1 → ok (the game advanced) +POST down=action-attack&up=action-attack → "not a legal move here" +``` + +So the socket, the token guard, `resolve`, and command dispatch are all +correct. The defect is in the emitted page. + +**`seat-{n}` ids exist only on the ~26 px circles inside the relationship +graph** (``, `doc.rs:185`). The visible *seats* section +cards are emitted by `player_card` as `
` +with **no id**. Every action card says *"drag Attack onto a seat, a +problem, or the table"*, and the seat it points at is not a target. + +`#table`, `problem-{n}` and `freedom-{n}` are real targets, so the page is +not wholly inert — the most natural target is. + +## Why every test in the repo passed + +`jsrun::gesture(html, down, up)` calls `__down(id)`, which synthesizes +`{ target: { id: id } }`. **It feeds element ids straight in and never +hit-tests.** So it establishes *"the script posts the ids it was given"* +and never *"the page has an identified element where the instruction says +to drop."* + +The 42-path coverage gate is blind for a related reason: it asserts each +view field is **present in the parsed document**, which a `
` with no +id satisfies perfectly. + +CB-WP-0015 closed a clause whose test was named for a property it did not +check. This is the same shape one layer out — a harness answering a +narrower question than its name implies — and it is the argument for +having kept the human check as a blocker rather than closing stage 1 on +green tests two passes ago. + +## Task: make the seat a drop target, and keep ids unique + +```task +id: CB-WP-0016-T01 +status: todo +priority: high +``` + +The seat card must carry the id `affordance()` already names for it. +`seat-{n}` is currently taken by the SVG node, and duplicate ids are +invalid HTML — so one of the two has to change, and **both must remain +droppable.** The graph circle works today and someone will have learned it. + +Decide where the aliasing lives. Rust decides what a pointer fact means +(ADR-0007 D5), so widening what `resolve` accepts is in bounds and +inventing a new id vocabulary in the page is not. + +**Control:** dropping on the seat *card* and dropping on the graph *node* +must produce the same command, and a test must go red if either stops +resolving. + +## Task: a gesture that lands nowhere must say so + +```task +id: CB-WP-0016-T02 +status: todo +priority: high +``` + +`SCRIPT` currently does `if (!down || !up) { down = null; return; }` — no +POST, no status line, nothing. That silence is why this defect survived a +human sitting in front of it. + +The Rust side already holds 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 correct. Refusing +**silently** is not. + +Make the outcome visible. Keep ADR-0007 control 5 intact — whatever the +page reports must still be raw pointer facts with no game vocabulary, and +the existing body-shape assertion must still hold. + +## Task: the check that closes the class + +```task +id: CB-WP-0016-T03 +status: todo +priority: high +``` + +**For every legal command, both ids `affordance()` produces must occur in +the emitted document.** Pure Rust, no JS engine, no browser, deterministic. +It fails on today's tree and it would have caught this the day the +renderer was written. + +This is the deliverable that matters — T01 fixes one instance, this closes +the class. An affordance naming an element that does not exist is exactly +the harness-does-nothing shape, and it currently has no detector at all. + +Then `evidence/CB-EV-0014-*.md`: + +- **What the human check bought**, priced honestly against what it cost to + keep stage 1 open for two passes. +- **Whether stage 1 closes now.** It needs the maintainer to drag again; + say so rather than closing it on a passing test, which is the failure + mode this whole pass is evidence for. +- **Quote CB-WP-0015's final cost, not this pass's** — and note that + CB-EV-0013 §5 found the self-quoting rule fixes the wrong boundary, so + say plainly whether the figure has settled. +- **Chaos: declaration 11 of 12.** The calibration window closes next + declaration.