CB-WP-0012-T01: survey the render port — and find AM-4a mis-instrumented
Measured with dep-weight.py's own method so the numbers subtract. Marginal cost against the 23-crate base graph, not headline size: egui+eframe 2,782,849 742x headroom wgpu+winit 1,741,979 465x softbuffer+tiny-skia 1,147,081 306x ratatui+crossterm 1,067,013 285x macroquad 480,501 128x HTML/SVG emission 0 0x ratatui costing more than macroquad is the surprise: rustix pulls linux-raw-sys at 479,901 lines. Separately, AM-4a counts proc-macro crates that never reach a binary: 89,048 lines, 36.2% of the shipped-runtime figure, syn alone 66,916. Real headroom is 92,798, not the 3,750 this repo has been citing. The recommendation is unchanged by that correction — macroquad is 5.2x over even at 92,798 — which is the only condition under which a survey may propose correcting a number in its own favour. The metric fix is filed separately, not bundled with the decision it unblocks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
331e7e9044
commit
53abbaf68f
2 changed files with 208 additions and 1 deletions
183
research/CB-RES-0006-render-port.md
Normal file
183
research/CB-RES-0006-render-port.md
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
# CB-RES-0006 — the render port, and what a 2D table costs
|
||||
|
||||
CB-WP-0012 T01. Step 1 of the inner loop. Measured 2026-08-02 at
|
||||
`331e7e9`.
|
||||
|
||||
The question: INTENT stage 1 needs `cb-render-api` and a first real
|
||||
implementation. AM-4a reports **3,750 lines of headroom** (246,250 of a
|
||||
250,000 target). Is that a real obstacle, or an artifact of the
|
||||
instrument?
|
||||
|
||||
Both, and not in the proportions I expected.
|
||||
|
||||
---
|
||||
|
||||
## 1. Method
|
||||
|
||||
Every figure below comes from the same method `tools/dep-weight.py` uses
|
||||
for AM-4a: resolve a real dependency graph with
|
||||
`cargo tree --edges normal --prefix none`, drop path dependencies, and
|
||||
count `\n` in every `.rs` file of the vendored source. Reusing the method
|
||||
verbatim is the point — a survey that measures candidates one way and the
|
||||
budget another produces two numbers that cannot be subtracted.
|
||||
|
||||
Probe crates were built in a scratch directory, one per candidate, and
|
||||
resolved against the live registry. The script is
|
||||
`$CLAUDE_JOB_DIR/tmp/survey/measure.py`; it is not committed, because a
|
||||
throwaway probe promoted to a tool is how the second-use rule gets broken
|
||||
by accident.
|
||||
|
||||
**Marginal, not total.** A candidate's headline size overstates its cost
|
||||
by whatever it already shares with the base graph. What AM-4a charges is
|
||||
the delta.
|
||||
|
||||
## 2. What a 2D table costs
|
||||
|
||||
Base: `games-ground --no-default-features`, 23 third-party crates,
|
||||
246,250 lines.
|
||||
|
||||
| candidate | total | shared with base | **marginal** | vs 3,750 headroom |
|
||||
|---|---:|---:|---:|---:|
|
||||
| `egui` + `eframe` | 2,946,121 | 163,272 | **2,782,849** | 742× |
|
||||
| `wgpu` + `winit` | 1,866,820 | 124,841 | **1,741,979** | 465× |
|
||||
| `softbuffer` + `tiny-skia` + `winit` | 1,271,922 | 124,841 | **1,147,081** | 306× |
|
||||
| `ratatui` + `crossterm` | 1,146,363 | 79,350 | **1,067,013** | 285× |
|
||||
| `egui` alone (no windowing) | 778,067 | 110,542 | **667,525** | 178× |
|
||||
| `macroquad` | 480,729 | 228 | **480,501** | 128× |
|
||||
| HTML/SVG emission, no Rust toolkit | — | — | **0** | 0× |
|
||||
|
||||
The cheapest candidate that opens a window costs **128 times** the entire
|
||||
remaining budget. This is not a near miss to be negotiated; it is two
|
||||
orders of magnitude.
|
||||
|
||||
**A surprise worth recording:** `ratatui` — a *terminal* library, the
|
||||
option one reaches for expecting it to be the cheap one — costs more than
|
||||
`macroquad`, a 2D game framework with a GPU backend. The reason is
|
||||
`linux-raw-sys` at 479,901 lines and `libc` at 129,990, pulled through
|
||||
`rustix`. Neither is code anyone audits by hand; both are largely
|
||||
generated constant and type definitions. Which raises the next question.
|
||||
|
||||
## 3. The instrument is wrong, and has been all along
|
||||
|
||||
AM-4a counts the `--edges normal` graph. That graph includes **proc-macro
|
||||
crates**, which run in the compiler and never reach a shipped binary.
|
||||
|
||||
| `games-ground --no-default-features` | crates | lines |
|
||||
|---|---:|---:|
|
||||
| as AM-4a measures it | 23 | **246,250** |
|
||||
| excluding proc-macro crates (`--edges normal,no-proc-macro`) | 18 | **157,202** |
|
||||
| the difference | 5 | **89,048** (36.2%) |
|
||||
|
||||
The five are `syn`, `quote`, `proc-macro2`, `unicode-ident` and
|
||||
`serde_derive`. `syn` alone is 66,916 lines — the single largest entry in
|
||||
the shipped-runtime figure, and it is a parser for Rust source that exists
|
||||
only at build time.
|
||||
|
||||
**So 36.2% of what AM-4a calls "what a game ships" is not shipped.** Under
|
||||
a corrected metric the headroom is not 3,750 but **92,798**. Every pass
|
||||
that has cited the 3,750 figure — including CB-WP-0011's own reasoning for
|
||||
deferring this declaration — cited a number that was wrong in the
|
||||
conservative direction.
|
||||
|
||||
**This does not change the recommendation, and that is the point of
|
||||
checking it before recommending.** Under the corrected metric `macroquad`
|
||||
still costs 5.2× the available headroom, and every other windowed
|
||||
candidate is worse. The dependency argument survives the correction of the
|
||||
number it rests on, which is the only condition under which a survey may
|
||||
propose correcting a number in its own favour.
|
||||
|
||||
The correction is proposed as a separate, narrow change — the metric
|
||||
should measure what ships — and it must not be bundled with the render
|
||||
decision, because a budget correction that arrives attached to the request
|
||||
it unblocks is indistinguishable from motivated reasoning even when it is
|
||||
right.
|
||||
|
||||
## 4. What stage 1 actually asks for
|
||||
|
||||
> *card/token/hand/relationship-graph visualization, drag-to-propose,
|
||||
> debug inspector, hot-seat play*
|
||||
|
||||
Read plainly: draw a table, let a human point at it, and let several
|
||||
humans take turns at one machine. It does **not** ask for a GPU, a frame
|
||||
loop, physics, or 60fps. Those are stage 2 (*"Physical 3D tabletop —
|
||||
wgpu renderer, Rapier-backed physics"*), and stage 2 is where the 1.7M
|
||||
lines of `wgpu` + `winit` get argued for on their merits.
|
||||
|
||||
Buying a GPU toolkit now would mean paying stage 2's dependency bill to
|
||||
satisfy stage 1's requirements, one stage early, against a budget that
|
||||
cannot cover it, and before the port interface has ever had a second
|
||||
implementation to be validated against.
|
||||
|
||||
## 5. The option that is not a Rust toolkit
|
||||
|
||||
Emit HTML with inline SVG and inline JavaScript; open it in the browser
|
||||
the machine already has.
|
||||
|
||||
- **visualization** — SVG draws cards, tokens and a relationship graph
|
||||
natively, and the relationship graph is the one element every Rust 2D
|
||||
toolkit would have made us hand-roll anyway.
|
||||
- **drag-to-propose** — pointer events in the emitted page.
|
||||
- **debug inspector** — already shipped (CB-WP-0011), and its renderer is
|
||||
already a total function from a projection to text; HTML is a second
|
||||
output format for the same walk.
|
||||
- **hot-seat play** — one browser tab, seats taking turns, the same
|
||||
commit/reveal the CLI player uses.
|
||||
|
||||
**Marginal AM-4a cost: zero.** Not "small" — zero. Emitting HTML is string
|
||||
formatting; the 2,897-line `svg` crate measured above is not needed and is
|
||||
not proposed. The input path costs no dependency either: a loopback
|
||||
HTTP/1.1 listener sufficient for `GET /` and `POST /command` is
|
||||
`std::net::TcpListener` plus roughly 250 lines of our own code, and our
|
||||
own code is not what AM-4a governs.
|
||||
|
||||
This is the central rule applied literally — *own the semantics; assimilate
|
||||
the implementation*. The browser is the most thoroughly assimilated 2D
|
||||
renderer available, and it is the one implementation we are guaranteed not
|
||||
to have to vendor, audit, or keep on a target.
|
||||
|
||||
**Own-code cost is a real cost.** ~250 lines of hand-rolled HTTP is code
|
||||
we own, test and carry, and hand-rolled HTTP has a bad security history.
|
||||
It listens on loopback only and speaks to a page it emitted itself, which
|
||||
bounds the exposure but does not erase it. That trade — 250 own lines
|
||||
against 480,501 third-party ones — is the decision ADR-0007 has to make
|
||||
explicitly rather than inherit from this survey.
|
||||
|
||||
## 6. What this cannot do
|
||||
|
||||
Stated concretely, because the adversarial review (T02) should not have to
|
||||
discover it:
|
||||
|
||||
- **No frame loop.** Animation, drag *feedback* at pointer-event rates,
|
||||
and anything requiring sustained redraw are the browser's problem, not
|
||||
the port's — the Rust side emits state and receives commands. If stage 1
|
||||
later wants tweened card motion, it is written in the emitted JS, in a
|
||||
language this repo otherwise does not use and has no gates for.
|
||||
- **No offline binary.** A game that "ships" ships a process that wants a
|
||||
browser. For a headless-first rules engine that is nearly free; for a
|
||||
consumer product at stage 4 it is not.
|
||||
- **A second language enters the build.** JavaScript in emitted strings is
|
||||
untested by `cargo test` and invisible to every gate this repo has.
|
||||
This is the strongest argument against, and the one T02 should press.
|
||||
- **It is not stage 2's renderer.** Nothing here carries forward to wgpu
|
||||
except the port interface — which is the argument *for* doing it, under
|
||||
the second-use rule, but only if the interface is written to be
|
||||
implemented twice rather than written around HTML.
|
||||
|
||||
## 7. Recommendation
|
||||
|
||||
1. **`cb-render-api`** — a canonical port over the existing `Project`ion:
|
||||
state in, drawable primitives out, proposed commands back. No
|
||||
assimilated types cross it (ArchitectureBlueprint §Never leak).
|
||||
2. **`cb-render-null`** — the reference no-op, so tests and agents need
|
||||
neither GPU nor browser.
|
||||
3. **`cb-render-html`** — the first real implementation, marginal AM-4a
|
||||
cost zero.
|
||||
4. **`cb-render-wgpu` is stage 2's**, and is where the port interface gets
|
||||
its second use and only then becomes canonical.
|
||||
5. **AM-4a's proc-macro defect is filed separately** and is not a
|
||||
precondition for any of the above.
|
||||
|
||||
**The honest summary:** the constraint that looked like it would force
|
||||
this decision turns out to be overstated by 36%, and the decision does not
|
||||
change. The recommendation is what it would have been at 92,798 lines of
|
||||
headroom, which is the only reason it is worth trusting at 3,750.
|
||||
|
|
@ -46,7 +46,7 @@ obstacle or an artifact of how AM-4a is instrumented.
|
|||
|
||||
```task
|
||||
id: CB-WP-0012-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
|
|
@ -74,6 +74,30 @@ that has been true for every pass that has cited it. Check this before
|
|||
recommending any budget change — a survey that argues for raising a target
|
||||
it has not first audited is arguing in its own favour.
|
||||
|
||||
**Done 2026-08-02.** [CB-RES-0006](../research/CB-RES-0006-render-port.md).
|
||||
Two findings, and they point opposite ways.
|
||||
|
||||
The cheapest candidate that opens a window (`macroquad`) costs a marginal
|
||||
**480,501** lines — **128×** the headroom. `egui` + `eframe` costs
|
||||
2,782,849. `ratatui`, the option one expects to be cheap, costs *more than
|
||||
macroquad* (1,067,013) because `rustix` pulls `linux-raw-sys` at 479,901
|
||||
lines.
|
||||
|
||||
And AM-4a is mis-instrumented: **36.2% of the shipped-runtime figure
|
||||
(89,048 lines) is proc-macro crates** — `syn` alone is 66,916 — which run
|
||||
in the compiler and never reach a binary. Real headroom is **92,798**, not
|
||||
3,750. Every pass that cited 3,750, this workplan's own Purpose included,
|
||||
cited a number wrong in the conservative direction.
|
||||
|
||||
The recommendation survives the correction: `macroquad` is still 5.2× over
|
||||
at 92,798. That was the condition for proposing the correction at all, and
|
||||
the correction is filed separately rather than bundled with the decision
|
||||
it would unblock.
|
||||
|
||||
Recommended: `cb-render-api` + `cb-render-null` + **`cb-render-html`** —
|
||||
emitted HTML/SVG/JS, marginal AM-4a cost **zero**, with `cb-render-wgpu`
|
||||
left to stage 2 where it becomes the interface's second use.
|
||||
|
||||
## Task: adversarial review of the survey
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue