← Design · c140

findings.md

c140 findings: the QGUIkit surface, scored

2026-11-12. Read at qnative main e609f42, design main 4453a8c085 (5e12709e36 by the last reader, after the c139 merge), the layer at e7e1454. The rubric is in the plan, written before the evidence. The full evidence, file by file, is on the report pages: Two Surfaces, Scored · The Cost of Two Surfaces · Skills and Tools. Their data is app/data/c140-evaluation.ts.

The verdict

A fast, faithful port with honest data boundaries, held together by attention. Where a machine holds the work, it is sound: the crate graph, the generated types, the worlds' contract test, the stream vectors, the budget. Where a person was meant to remember, it is soft:

  • type set by 433 literals;
  • one slot kind of three;
  • keyboard access in one component;
  • half the names changed;
  • a scorecard stale within a day;
  • public docs that do not know the second surface exists.

The correction is not a rewrite. It is to move the eye-held half to machines, and to start writing down where the surfaces differ on purpose.

No row scores 5, three score 4, eleven score 3, six score 2.

The scores

Scale: 5 by construction · 4 in practice · 3 mostly, with named exceptions · 2 only where someone was careful · 1 does not hold. Trend is the QGUIkit surface against the Vue surface on the same principle.

#PrincipleScoreTrendIn a line
1Modularity3sameReal units and a clean crate boundary. Four of the biggest files are several components and an engine each.
2Clean data boundaries4betterThe layer cannot see a source (5). The pages go page → repository → nozzle, with four named leaks (3).
3Composition over configuration3worseDataMachine kept its six slots. Elsewhere the builder pattern pulled toward flags, typed children and pre-built cells.
4View complexity contained3worseThe table owns its hard internals. The page wires back sort, filter, columns and the sticky band.
5Low knowledge of context3sameNo component knows the rail or the window. Several know the page's margins and the viewport.
6Slots3worseGPUI has all three kinds. We use named slots (4); the default (1) and scoped slots (1) are not used.
7Brand tokens2worseColour travels and matches today (4). Type, radius and spacing do not travel (1). Nothing notices drift.
8Two surfaces2n/aData contracts are machine-held (4). Components and pages are held by hand and eye (2). Divergences are not written down (1).
9Functional; colour is semantic3sameVariants, not colours. The variant sets are too small, so pages paint their own.
10Native to the medium3sameReads as GPUI, but five private traits reimplement .when().
11Progressive enhancement3sameThe bases are simple. Missing small primitives get redrawn per page.
12Accessibility by default2worseThe table is accessible; nothing else is. GPUI gives nothing for free.
13States are explicit2worseHover only (2). The data states have one shape but are drawn per page and never tested (3).
14Replicate first4sameEvery page was measured before it was drawn, and every oddity was decided. The record was nearly lost.
15The demo is the test3sameEvery state is drawn. Only the newest pages are real tests.
16Size and motion3sameNamed size steps. Neither surface has motion tokens, and the written rule is out of date on both.
17The unified pantry3sameThe vocabulary is in the layer. Small pantry pieces sit in the pages, and domain data sits in the layer.
18Documentation architecture2worseGood native docs, in the wrong place: inside the library and inside developer mode.
19Naming2worseThe crate promises the web's names and keeps about half.
20The budget4betterA principle the web never wrote down, kept best of all.

Rows 9–20 are the ones the prompt asked us to find in our docs:

  • 12, 13, 16: component_philosophy.md principles 5 and 6, and its design guidelines.
  • 11, 14, 15: principles 4, 7 and 8.
  • 17, 18, 19: its scope, documentation and naming sections.
  • 10: Tailwind-native, read for GPUI.
  • 20: new, from the medium.

How cumbersome are two surfaces? The honest answer

Not drift hell, not yet. The surfaces are two days apart, the data contracts are generated, and porting is fast: 11 to 23 minutes per Inventory listing once the layer existed. The cost is in maintenance, and it grows with every web change:

  • A change lands in many places. A component's look lands in 9–10 places across three repos, a new column in 9–11, a sentence of copy in 2–4.
  • Nothing tells the twin. No machine notices when a Vue component changes and its Rust twin does not.
  • Four drifts in three days:
    • the rail grew its own look;
    • the bridge fixture forked;
    • the Tools widths differ with no record;
    • the layer's in-flight restyles have no path to their twins.
  • Logic is held by hand three times: the formatters, the rollups (a Rust port re-ported to TypeScript) and the request key.
  • Fixes travel one way. The port fixed web bugs and none went back.

The danger is silent drift, not effort. The fix is a parity register both surfaces read, and vectors for the shared logic.

What to do

Now (small):

  1. Keep the ten port specs. They were stranded in a session scratchpad under /private/tmp; copied to rescued-specs/ on 2026-11-12, uncommitted. Their home is qnative c2.
  2. Sync the bridge fixture before anyone runs sync-bridge.sh.
  3. Fix six small flaws:
    • the copied .qp() in harness.rs:280;
    • two dark-mode colours;
    • the inbox's error drawn as empty;
    • cycle ids in the Sensor page's copy;
    • two stale scorecard rows.
  4. Add a token drift check to qnative's CI.

To rule: items 5–13 in review stop 1. The largest are the parity register with dual-surface docs (the Surface glossary entry's ask), tokens for type, radius and motion with a lint, the three slot equivalents, and the keyboard pass.

Skills and tools: ten candidates on the tools page. First come a /native-port skill that carries the whole loop, a spec measurer that writes the spec as data, and the parity register as a check.

Qpoint Brand Style Guide