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 <noreply@anthropic.com>
This commit is contained in:
parent
d11e02e32b
commit
4df2d0a4ae
1 changed files with 136 additions and 0 deletions
136
workplans/CB-WP-0016-the-drop-target.md
Normal file
136
workplans/CB-WP-0016-the-drop-target.md
Normal file
|
|
@ -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** (`<g id="seat-0">`, `doc.rs:185`). The visible *seats* section
|
||||
cards are emitted by `player_card` as `<div class="card" data-viewer=…>`
|
||||
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 `<div>` 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue