clay-borg/history/260802-render-port-research.md
tegwick c5fa610e59 CB-WP-0012-T02: adversarial review — not approvable as written
Four of six challenges conceded; the survey's two main arguments are
replaced rather than clarified.

C1  The survey concluded the sub-100k region was empty without ever
    measuring it. It is not empty: tiny-skia fits at 83,956, and the
    real windowed floor is fltk at 140,079 — 1.5x corrected headroom,
    not 128x. "Two orders of magnitude" is withdrawn.

C1b Following that concession: wgpu+winit is 1,741,979 marginal lines
    against a 250,000 target. AM-4a is incompatible with INTENT stage 2
    by 7x, and has been since both were written. Raised for the
    maintainer; deliberately not decided by this pass.

C2  "Marginal cost zero" was scored on an axis chosen to produce zero,
    and the same relocation already flattered sdl2 and fltk in the
    survey's own table. One acquisition rule now covers all three, and
    it raises two candidates' cost rather than lowering the winner's.

C3  The survey contradicted itself on the second-use rule. cb-render-api
    and cb-render-null are withdrawn; cb-render-html ships against the
    existing Project trait, and the port waits for stage 2's second use.

C6  The candidate measurements did carry a positive control. The C1
    batch did not — it copied dep-weight.py's measurement function
    without its guards — and was re-measured under them before being
    cited. Figures reproduced unchanged. The HTML row is relabelled
    0 by construction, not by measurement.

C4/C5 become six controls binding on ADR-0007.

The recommendation stands. The reasoning that reached it does not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 04:12:22 +02:00

3.9 KiB

260802 — how CB-RES-0006 was conducted

Unpolished trail for research/CB-RES-0006-render-port.md, per InnerLoop §Step 2 documentation requirement.

What was measured vs cited

Everything quoted is measured. No line count in the survey comes from a README, a crates.io page, or memory. Method: a scratch cargo crate per candidate, cargo add, then cargo tree --edges normal --prefix none over the resolved graph, then \n counted in every .rs file of the vendored source under ~/.cargo/registry/src/*/.

That method is copied out of tools/dep-weight.py rather than re-invented, because the survey's whole purpose is to subtract candidate sizes from a budget produced by that tool, and two methods produce two numbers that cannot be subtracted.

Scripts: $CLAUDE_JOB_DIR/tmp/survey/{measure,breakdown,gap}.py. Deliberately not committed — a throwaway probe promoted to a repo tool is how the second-use rule gets broken by accident. If a later pass needs these numbers refreshed, it should re-derive them, and the fact that it costs ten minutes is a feature.

Dead ends and corrections during the survey

First reading was on the wrong axis. The initial run reported headline totals: egui+eframe 2,946,121, ratatui+crossterm 1,146,363, macroquad 480,729. Those are the numbers a survey normally quotes and they are the wrong ones — AM-4a charges what a candidate adds to a graph that already holds 23 crates. Recomputed as marginal against the real base. For macroquad the difference is negligible (480,729 → 480,501); for egui+eframe it is 163,272 lines of overlap.

A network 403 that was not a network failure. curl to crates.io returned 403; the sandbox looked like it was blocking egress. It was a missing user-agent — crates.io rejects requests without one. Two further probes (sparse index, static.crates.io) returned 200. Worth recording because "the environment is blocking me" is a conclusion that ends surveys, and it was wrong here by one header.

Checked whether cargo tree was including non-host targets. The ratatui figure was dominated by linux-raw-sys (479,901) and looked like it might be an all-platform artifact — a Windows-bindings inflation of a Linux measurement. Re-ran with --target x86_64-unknown-linux-gnu: identical, 1,146,363 both ways. The number is real, and ratatui really does cost more than a 2D game framework.

The proc-macro finding was not what the survey set out to look for. It came from asking why syn was the largest single entry in a "shipped-runtime" figure. --edges normal,no-proc-macro answered it: 89,048 lines, 36.2%, none of which reaches a binary.

Order matters here, and it went the right way by luck rather than design: the candidate measurements were complete before the instrument defect was found, so the recommendation could be checked against the corrected number instead of being derived from it. Had it gone the other way the survey would have been unusable — a budget correction discovered while looking for headroom is not evidence, whatever it says.

What the survey did not do

  • It did not sample the region between 0 and 480,501 lines. The candidate list was assembled from what one reaches for — modern GPU-era Rust GUI stacks — and every one of them landed two orders of magnitude over. The conclusion "no windowed toolkit fits" was therefore drawn from a sample that contained no small windowed toolkits. This is the gap the challenge (T02) attacks, and it is a real one.
  • It did not run any candidate. No candidate was built, drawn with, or benchmarked. Per InnerLoop §Step 1 the comparisons here are dependency cost only; nothing in the survey claims one toolkit draws better than another, and no better verdict is available on that axis.
  • It did not measure the browser. The HTML option is scored zero on AM-4a because AM-4a measures vendored Rust. What the browser costs on any other axis is unmeasured and the survey says so in §5.