ADR-0009: embed quick-js; node is refused. Measured marginal cost against the dev-toolchain graph, under the positive control: boa_engine 896,410 rquickjs 69,985 quick-js 11,434 node 0 <- and that zero is the problem ADR-0007 D3's acquisition rule biting its author. CI runs on rust:1.97, which has no node, so the test would make our build fetch a JS runtime of tens of millions of unaudited lines while scoring zero on the only instrument that governs dependencies. A browser is exempt because a developer has one regardless of us; a CI-installed runtime is not. The loop is now closed: the real server serves the real page, QuickJS runs that page's own scripts, the gesture goes over a real socket, and the seat's Choice comes back. Before this, every link was tested and the chain was not — a page whose JavaScript sent something else entirely would have passed everything. Three controls, each red for its stated reason: the JS posting a command name instead of ids, the gesture not being delivered (EXPECT-VACUOUS), and the token stripped from the endpoint. A wrong assertion worth keeping: the first draft required the body not to contain "attack". It legitimately does — action-attack is the id of an element a finger landed on. An element may name an action; that is not the page deciding. The real test is the shape: exactly two fields, down and up, carrying two ids and nothing derived from them. AND the ADR's own cost argument was wrong. It claimed 35% of AM-4b's headroom; after landing AM-4b did not move at all. It measures games-ground --edges normal — one package, no dev edges. Measured, the workspace including dev edges is 725,258 lines against AM-4b's 317,021: 408,237 uncounted, MORE THAN THE TARGET ITSELF (criterion, clap, ciborium, quick-js). The decision stands on the acquisition rule; the affordability argument is withdrawn. Third defect in the AM-4 family. Also fixed structurally rather than by raising a limit: `make status` had grown past its 40-line readability gate as workplans accumulated. Closed workplans now collapse to one line, so the report is fixed-size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
7.5 KiB
Markdown
182 lines
7.5 KiB
Markdown
---
|
|
id: CB-WP-0014
|
|
kind: product
|
|
title: "Execute the JavaScript, and close or keep stage 1"
|
|
status: todo
|
|
---
|
|
|
|
# Purpose
|
|
|
|
```
|
|
structural tier M (adds an external dependency to the toolchain —
|
|
InnerLoop v1.6 tier table)
|
|
chaos d4 = 2 → no override
|
|
declared tier M
|
|
```
|
|
|
|
Declaration 9 of 12. Tier M merges the survey and the ADR into one
|
|
document; the adversarial review is optional.
|
|
|
|
## The one thing blocking stage 1
|
|
|
|
CB-EV-0010 §4, stated plainly at the time:
|
|
|
|
> **The emitted JavaScript has never run.** Every test is on the Rust
|
|
> side: the socket loop is driven by synthetic HTTP, and the page is
|
|
> asserted against as a parsed document. No browser engine has executed
|
|
> `SCRIPT`.
|
|
|
|
All four of INTENT stage 1's named deliverables now exist. That sentence
|
|
is the only reason the stage is still open, and it has been carried
|
|
unexamined for one pass on the assumption that executing it was not
|
|
possible here.
|
|
|
|
**It is possible.** `node` v24.11.1 is on this machine. The assumption was
|
|
never checked, which makes it the same shape as every other finding in the
|
|
last three passes: a claim carried because nobody ran the one command that
|
|
would settle it.
|
|
|
|
## What executing it actually proves
|
|
|
|
Not that the table *looks* right — that needs a rendering engine and a
|
|
human eye, and neither is available here. What it proves is the contract
|
|
ADR-0007 control 5 rests on:
|
|
|
|
> the page reports **raw pointer facts and nothing else**, and Rust
|
|
> decides what command they mean.
|
|
|
|
That contract is currently asserted by a test that greps the emitted
|
|
script for game vocabulary. Grepping for the absence of words is a weak
|
|
proxy for "this code cannot construct a command". Running it, feeding it
|
|
synthetic pointer events, and observing exactly what it puts on the wire
|
|
is the real check.
|
|
|
|
## Task: decide whether a JS engine joins the toolchain
|
|
|
|
```task
|
|
id: CB-WP-0014-T01
|
|
status: done
|
|
priority: high
|
|
```
|
|
|
|
Write `decisions/ADR-0009-*.md` (tier M: survey and decision in one).
|
|
|
|
**This is a real dependency decision, not a formality.** CI runs on the
|
|
`rust:1.97` image, which has no `node`. Adding a test that needs one means
|
|
either installing it in CI or letting the test skip — and **a test that
|
|
skips silently is the harness-does-nothing class this project has found
|
|
five times.** If it can skip, it must fail loudly instead.
|
|
|
|
Decide, and price it under ADR-0007 Decision 3's acquisition rule — the
|
|
rule this project adopted precisely so that "it's only a dev tool" is not
|
|
an automatic pass:
|
|
|
|
- `python3` is already a toolchain dependency, so this is the second, not
|
|
the first. Say whether that makes it cheaper or is merely a precedent
|
|
being leaned on.
|
|
- What the alternatives cost: no execution at all (status quo, and the
|
|
reason stage 1 is open), or a JS interpreter as a Rust crate (measure it
|
|
— do not estimate, and note that AM-4b now has an unmeasured proc-macro
|
|
share of its own).
|
|
- Whether the engine is required by `make all` or by a separate target.
|
|
|
|
**AM-4b is the budget this lands in**, and it is the instrument
|
|
CB-WP-0013 explicitly declined to correct. Do not use its uncorrected
|
|
headroom as an argument.
|
|
|
|
**Done 2026-08-02.**
|
|
[ADR-0009](../decisions/ADR-0009-embed-the-js-engine.md) — **embed
|
|
`quick-js`; `node` is refused.** Measured, marginal, under the control:
|
|
`boa_engine` 896,410 · `rquickjs` 69,985 · **`quick-js` 11,434** · `node`
|
|
**0**, and that zero is the problem.
|
|
|
|
This is ADR-0007 D3's acquisition rule biting its author. CI runs on
|
|
`rust:1.97`, which has no `node` — so the test would make *our build*
|
|
fetch a JS runtime of tens of millions of unaudited lines, scoring zero on
|
|
the only instrument that governs dependencies. A browser is exempt because
|
|
a developer has one regardless of us; a CI-installed runtime is not.
|
|
|
|
**And the cost argument in the first draft was wrong.** It claimed 35% of
|
|
AM-4b's headroom. After landing, AM-4b did not move at all — it measures
|
|
`games-ground --edges normal`, one package, no dev edges. Measured: the
|
|
workspace including dev edges is **725,258** lines against AM-4b's
|
|
**317,021**, so **408,237 lines are uncounted — more than the target
|
|
itself**. The decision stands on the acquisition rule; the affordability
|
|
argument is withdrawn, because there is none to be had until the
|
|
instrument can see what it is buying. Filed as the third AM-4 defect.
|
|
|
|
## Task: run it, end to end, through a real socket
|
|
|
|
```task
|
|
id: CB-WP-0014-T02
|
|
status: done
|
|
priority: high
|
|
```
|
|
|
|
Execute `cb_render_html::doc::SCRIPT` in a real JS engine against a
|
|
minimal DOM, synthesize a pointer-down and pointer-up on two real element
|
|
ids from a real emitted document, and let its `fetch` reach the **actual
|
|
`Server`** — the same `next_choice` loop `cb-play --serve` runs.
|
|
|
|
Acceptance: the seat's `Choice` comes back from a pointer gesture that
|
|
originated in JavaScript. That is the first time anything in this project
|
|
has closed that loop.
|
|
|
|
**Controls, and they are the point.** The test must go red when:
|
|
|
|
- the JS is mutated to POST a command name instead of element ids — this
|
|
is **control 5 asserted rather than grepped**, and if it does not fire,
|
|
the grep test was the only thing holding the contract up;
|
|
- the pointer events are not delivered at all — the EXPECT-VACUOUS case, a
|
|
harness that runs a script and asserts nothing about what it did;
|
|
- the token is stripped from the endpoint the page was given.
|
|
|
|
**Done 2026-08-02.** `crates/cb-render-html/src/jsrun.rs` and
|
|
`hotseat::tests::a_gesture_in_javascript_becomes_a_move_in_the_game`.
|
|
|
|
**The loop is closed.** The real server serves the real page; QuickJS runs
|
|
*that page's own scripts*; the gesture it produces goes over a real socket;
|
|
the seat's `Choice` comes back. Both `<script>` blocks are lifted from the
|
|
served document, so the endpoint under test is the one the page actually
|
|
carried — token included.
|
|
|
|
Three controls, each red for its stated reason:
|
|
|
|
| mutation | result |
|
|
|---|---|
|
|
| the JS posts `command=SelectAction&target=…` | body assertion red — **control 5 asserted, not grepped** |
|
|
| the gesture is not delivered (EXPECT-VACUOUS) | `expected exactly one fetch, got []` |
|
|
| the token dropped from the endpoint | the end-to-end token assertion red |
|
|
|
|
**A wrong assertion, worth keeping.** The first draft asserted the body
|
|
must not contain `"attack"`. It legitimately does: the body is
|
|
`down=action-attack&up=seat-1`, and `action-attack` is *the id of an
|
|
element a finger landed on*. An element may name an action; that is not
|
|
the page deciding anything. The real test of reporting-versus-deciding is
|
|
the **shape** — exactly two fields, `down` and `up`, carrying two ids and
|
|
nothing derived from them. That is what it asserts now.
|
|
|
|
## Task: close stage 1, or say what still blocks it
|
|
|
|
```task
|
|
id: CB-WP-0014-T03
|
|
status: todo
|
|
priority: high
|
|
```
|
|
|
|
`evidence/CB-EV-0012-*.md`, and then a decision on INTENT.
|
|
|
|
**Be precise about what is and is not established.** A passing JS test
|
|
proves the input contract and the loop. It does **not** prove that an SVG
|
|
table renders legibly, that a drag feels like a drag, or that a human can
|
|
play a game — none of which any test here can reach.
|
|
|
|
State which of stage 1's four deliverables are evidenced and which rest on
|
|
inspection, then close the stage or name the remaining blocker. Carrying
|
|
it open by default is as unexamined as closing it by default was going to
|
|
be.
|
|
|
|
**Adopt or reject the self-quoting rule** proposed in CB-EV-0011 §4: an
|
|
evidence file quotes the previous pass's final cost and never its own.
|
|
This pass is the first that can apply it — CB-WP-0013's figure is now
|
|
final. Decide it rather than inheriting it.
|