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.
| # | Principle | Score | Trend | In a line |
|---|---|---|---|---|
| 1 | Modularity | 3 | same | Real units and a clean crate boundary. Four of the biggest files are several components and an engine each. |
| 2 | Clean data boundaries | 4 | better | The layer cannot see a source (5). The pages go page → repository → nozzle, with four named leaks (3). |
| 3 | Composition over configuration | 3 | worse | DataMachine kept its six slots. Elsewhere the builder pattern pulled toward flags, typed children and pre-built cells. |
| 4 | View complexity contained | 3 | worse | The table owns its hard internals. The page wires back sort, filter, columns and the sticky band. |
| 5 | Low knowledge of context | 3 | same | No component knows the rail or the window. Several know the page's margins and the viewport. |
| 6 | Slots | 3 | worse | GPUI has all three kinds. We use named slots (4); the default (1) and scoped slots (1) are not used. |
| 7 | Brand tokens | 2 | worse | Colour travels and matches today (4). Type, radius and spacing do not travel (1). Nothing notices drift. |
| 8 | Two surfaces | 2 | n/a | Data contracts are machine-held (4). Components and pages are held by hand and eye (2). Divergences are not written down (1). |
| 9 | Functional; colour is semantic | 3 | same | Variants, not colours. The variant sets are too small, so pages paint their own. |
| 10 | Native to the medium | 3 | same | Reads as GPUI, but five private traits reimplement .when(). |
| 11 | Progressive enhancement | 3 | same | The bases are simple. Missing small primitives get redrawn per page. |
| 12 | Accessibility by default | 2 | worse | The table is accessible; nothing else is. GPUI gives nothing for free. |
| 13 | States are explicit | 2 | worse | Hover only (2). The data states have one shape but are drawn per page and never tested (3). |
| 14 | Replicate first | 4 | same | Every page was measured before it was drawn, and every oddity was decided. The record was nearly lost. |
| 15 | The demo is the test | 3 | same | Every state is drawn. Only the newest pages are real tests. |
| 16 | Size and motion | 3 | same | Named size steps. Neither surface has motion tokens, and the written rule is out of date on both. |
| 17 | The unified pantry | 3 | same | The vocabulary is in the layer. Small pantry pieces sit in the pages, and domain data sits in the layer. |
| 18 | Documentation architecture | 2 | worse | Good native docs, in the wrong place: inside the library and inside developer mode. |
| 19 | Naming | 2 | worse | The crate promises the web's names and keeps about half. |
| 20 | The budget | 4 | better | A 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.mdprinciples 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):
- Keep the ten port specs. They were stranded in a session scratchpad under
/private/tmp; copied torescued-specs/on 2026-11-12, uncommitted. Their home is qnative c2. - Sync the bridge fixture before anyone runs
sync-bridge.sh. - Fix six small flaws:
- the copied
.qp()inharness.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.
- the copied
- 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.