UI interaction baselines registry
SSOT for the latency of reusable UI interaction primitives (open a menu, create a tab, focus an input, …) across Koder surfaces, measured as request→painted and compared on the same run against the best-in-class external equivalent (Chrome, GNOME, Tilix, …).
Contract: meta/docs/stack/specs/testing/ui-interaction-perf.kmd. Ratified by meta/docs/stack/rfcs/stack-RFC-012-ui-interaction-perf-corpus.kmd.
This is NOT
perf-baseline.md. That file is an append-only log of backend hot-path bench runs keyed by component. This file is a tiered-threshold table keyed by(sdk-component, primitive, surface, target), owner-seeded, ratio-primary, and (from stack-RFC-012 Phase 5) used as a gate. Do not merge the two.
How to read a row
- ratio =
koder_p99 / reference_p99measured in the *ame run on thesame machine*— the portable signal (spec R5).
—when no external equivalent (self-referential primitive). - koder p99 / ref p99 = raw tail latency, ms, hardware-tagged. Median
(p50) lives in the per-run history, not here; this table holds the latest governing p99.
- tier =
ideal/acceptable/unacceptable(spec R6).seedinthe authority column means the tier is the HCI-prior default, not yet an owner verdict.
- authority =
seed(HCI default),owner(owner verdict — overrides),or
ref(derived from the measured reference). - reference = external software + version + capture date (spec R5).
- A row with no measurement yet carries
tier: seedfrom the defaultsbelow and
—for the measured columns.
Tier seed defaults (HCI priors — spec R6)
Used to seed every primitive before any measurement or owner verdict, so the corpus is never empty at bootstrap:
| Tier | Default condition |
|---|---|
| ideal | ratio ≤ 1.2 AND p99 ≤ 100 ms (RAIL "instant") |
| acceptable | 100 ms < p99 ≤ 1 s (Doherty attention bound) → auto-opens perf-debt ticket |
| unacceptable | p99 > 1 s OR ratio > 3 |
Schema
| Column | Meaning |
|---|---|
date |
YYYY-MM-DD of the run (UTC). |
sdk-component |
<area>/<sector> that owns the widget (spec R8). product:<path> if hand-rolled (promote-to-sdk finding). |
primitive |
Name from the closed vocabulary (spec R9). |
surface |
flutter-desktop / flutter-mobile / flutter-web / cef-desktop / web / tui. |
target |
OS/runtime tag where relevant (e.g. linux-x11, android). |
ratio |
koder_p99 / ref_p99, or —. |
koder p99 |
ms, tail. |
ref p99 |
ms, tail, or —. |
tier |
ideal / acceptable / unacceptable. |
authority |
seed / owner / ref. |
reference |
external sw + version + date, or —. |
hardware |
one-line hardware tag (raw ms is hardware-bound; ratio is not). |
notes |
perf-debt ticket ref, instrumentation status, follow-ups. |
Entries
First real-engine row landed in stack-RFC-012 Phase 2 (KKIT-078): the koder_kit desktop integration harness. Phase 3 adds the Kruze slice.
enginessdkkoder_kit — IDEAL (owner verdict, 2026-06-08)
Measured via the KoderInteractionProbe integration test on the real Flutter Linux engine under xvfb. Self-referential (no external reference → ratio —).
Owner verdict:
ideal(2026-06-08), data-backed by the KKIT-086 diagnostic. Thekoder p99ms below is integration_test HARNESS OVERHEAD, not app latency. The diagnostic (single-frame pumps + frame count) showed the result paints in frames p501 / max1 — the very next frame after the input (~16 ms, as fast as physically possible). The ~95-150 ms wall-clock is thetester.tap()+ live-bindingpumpcadence floor (~100 ms), NOT render. So the true request→painted latency is 1 frame → genuinelyideal(well under the 100 ms RAIL bar). See spec R4.1 (harness-floor caveat) + KKIT-086.KKIT-087 (frames-to-paint, all 4 measured): open-menu / focus-input / open-dialog / open-dropdown ALL paint in frames p501 / p991 / max=1 on dev-linux-dek — every primitive responds on the next frame. This is the clean governing metric (R4.1); the ms column is kept only as harness-floor evidence.
| date | sdk-component | primitive | surface | target | ratio | koder p99 (ms; harness-floored) | frames-to-paint | tier | authority | reference | hardware | notes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-08 | enginessdkkoder_kit | open-menu | flutter-desktop | linux-x11 | — | 113.7 | 1 | ideal | owner | — | dev-linux-dek (xvfb) | KKIT-079/086. Paints in 1 frame; the 113.7 ms is harness overhead (the earlier 507 ms was the settle-animation, dropped). |
| 2026-06-08 | enginessdkkoder_kit | focus-input | flutter-desktop | linux-x11 | — | 113.0 | 1 (measured) | ideal | owner | — | dev-linux-dek (xvfb) | KKIT-084086. Diagnostic measured frames p50=1max=1 — the representative proof the band is harness floor, not latency. |
| 2026-06-08 | enginessdkkoder_kit | open-dialog | flutter-desktop | linux-x11 | — | 105.8 | 1 | ideal | owner | — | dev-linux-dek (xvfb) | KKIT-085086. Overlayroute primitive; same 1-frame paint + harness floor. |
| 2026-06-08 | enginessdkkoder_kit | open-dropdown | flutter-desktop | linux-x11 | — | 133.2 | 1 | ideal | owner | — | dev-linux-dek (xvfb) | KKIT-085086. Overlayroute primitive; same 1-frame paint + harness floor. |
| 2026-06-08 | enginessdkkoder_kit | switch-tab | flutter-desktop | linux-x11 | — | — | 2 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-088 (Phase 6). Route-push primitive (build incoming page + paint) → 2 frames ≈33ms, well under the 100ms RAIL bar. Measured in an ISOLATED host (the shared corpus Column overflowed the fixed linux window and broke neighbouring overlay primitives). frames p502/p992/max=2 over 20 samples. seed tier pending an owner verdict. |
| 2026-06-08 | enginessdkkoder_kit | open-popover | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-090 (Phase 6, vocab completion slice 1). MenuAnchor opened by a PRIMARY-tap trigger button (real tap→paint path). frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | open-drawer | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-090. Scaffold drawer via ScaffoldState.openDrawer(). frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | expand-tree-node | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-090. ExpansionTile expand → child first-paint. frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | type-into-input | flutter-desktop | linux-x11 | — | — | 0 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-091 (Phase 6, vocab slice 2). TextField reflects input. 0 = tester.enterText pumps once internally so the glyph is painted within the intent (≤1 frame, instant). |
| 2026-06-08 | enginessdkkoder_kit | open-submenu | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-091. SubmenuButton inside an open MenuAnchor → nested item first-paint. frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | select-dropdown-item | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-091. Picking an item updates the bound value → repaint. frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | close-dialog | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-092. First DISMISSAL primitive. RESPONSIVENESS = structural removal with exit transition OFF: frames p501/p991max=1 (gated). The exit ANIMATION is a separate NON-GATED settle reference: `close-dialogsettle` = p508/p999/max=9 (~150ms designed fade). R2.1 first-response vs settled; D1 don't gate the animation. |
| 2026-06-08 | enginessdkkoder_kit | new-tab | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-093 (vocab COMPLETE). Stateful tab-manager host; adding a tab paints its view. frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | close-tab | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-093. Dismissal (R2.2); a tab strip has no exit animation so first-response =settle. frames p501/p991/max=1. |
| 2026-06-08 | enginessdkkoder_kit | tab-switch-view | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-093. IndexedStack swap; find skips offstage so the inactive view "appears" once switched onstage. frames p501/p991max=1. *616 closed vocab now measured on flutter-desktop.* |
| 2026-06-08 | enginessdkkoder_kit | open-context-menu | flutter-desktop | linux-x11 | — | — | 1 | ideal | seed | — | dev-linux-dek (xvfb) | KKIT-088 (Phase 6). MenuAnchor opened via MenuController.open() — 1-frame paint = instant. Trigger is programmatic, NOT a synthesised right-click: flutter_test -d linux does not deliver secondary-tap gestures, and the primitive's budget (R8) is the menu RENDER, not gesture recognition (a framework concern). frames p501/p991/max=1 over 20 samples. seed tier pending an owner verdict. |
enginessdkkoder_kit — flutter-web (cross-surface, Phase 6 — KKIT-089)
First cross-surface validation (stack-RFC-012 Phase 6's core premise: does the corpus generalise beyond one surface?). The SAME phase6_primitives_perf_test.dart runs on flutter-web (real Chromium 148 browser, flutter drive headless via chromedriver on dev-linux-dek). For flutter-web the metric is still frames-to-paint (the Flutter framework scheduler runs in the browser; the spec's PerformanceObserver is for the NON-Flutter web surface, R3).
Result:
All tests passed.on flutter-web — both primitives' perf-golden gates (frames p99 ≤ 3) PASS in the browser, AND the exact frames-to-paint are captured (KKIT-089 closed the precise-measurement gap). The numbers are identical to flutter-desktop:switch-tab= 2 frames,open-context-menu= 1 frame — the same SDK widgets paint in the same frame budget on the browser as on the desktop engine. The corpus generalises with equal numbers, not just an equal verdict.Extraction seam (the architectural win):
flutter drivedoes NOT relay the browser-sideprint(), so the harness exports measurements viaIntegrationTestWidgetsFlutterBinding.reportData, which the defaultintegrationDriver(responseDataCallback: writeResponseData)writes tobuild/integration_response_data.jsonon every surface uniformly — replacing fragile, surface-dependent print-scraping. This seam is reusable by every future driven surface (mobile/tv).
| date | sdk-component | primitive | surface | target | ratio | koder p99 | frames-to-paint | tier | authority | reference | hardware | notes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-08 | enginessdkkoder_kit | switch-tab | flutter-web | chromium-148 | — | — | 2 | ideal | seed | — | dev-linux-dek (chromium headless) | KKIT-089. Exact via reportData JSON: p502/p992/max=2 (20 samples). IDENTICAL to flutter-desktop. Gate p99≤3 PASSED. |
| 2026-06-08 | enginessdkkoder_kit | open-context-menu | flutter-web | chromium-148 | — | — | 1 | ideal | seed | — | dev-linux-dek (chromium headless) | KKIT-089. Exact via reportData JSON: p501/p991/max=1 (20 samples). IDENTICAL to flutter-desktop. Gate p99≤3 PASSED. |
productshorizontalkruze — pending Phase 3 (advisory)
Planned first rows (instrumentation unverified until the CEF OnPaint probe lands — spec R3):
| date | sdk-component | primitive | surface | target | ratio | koder p99 | ref p99 | tier | authority | reference | hardware | notes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-08 | product:productshorizontalkruze | new-tab | cef-desktop | linux-x11 | — | ~110-180 ms (coarse) | — | acceptable | seed | — | dev-linux-kruze optkruze-seat (cage headless, grim+wtype) | kruze#239e MEASURED. Latency ~110-180ms for 2-6 light tabs (grim-coarse, not seconds), then CRASH at ~7 tabs (webview_cef instability). Light tabs UNDERESTIMATE real pain. Root cause = webview/CEF contention under N tabs (eager init), not the new-tab render → fix kruze#240 (lazy webview init). |
| 2026-06-09 | enginessdkkoder_kit | open-menu | flutter-desktop | linux-x11 | 1.0 | 113.7 | 1 | ideal | seed | tilix 1f (headless XTEST) | dev-linux-dek (xvfb) | KKIT-095 /k-mirror D5. Tilix reference via XTEST synth + ffmpeg 60fps frames-to-paint: open-menu p991f over 5 samples. kterm=tilix, both ideal. |
| 2026-06-09 | enginessdkkoder_kit | new-tab | flutter-desktop | linux-x11 | 0.5 | — | 1 | ideal | seed | tilix 2f (headless XTEST) | dev-linux-dek (xvfb) | KKIT-095. Tilix new-tab p99=2f (samples 2,1,1,2,2); kterm 1f → kterm 2x faster. Both ideal (<<100ms RAIL). |
| 2026-06-10 | product:productshorizontalkruze | new-tab | cef-desktop | linux-wayland | — (self-ref) | 192 ms | — | acceptable | owner-pending | Chromium 1.x measured 2026-06-10 (NOT a meaningful equivalent — see note) | dev-linux-kruze optkruze-seat | kruze#239c. Self-referential per spec R5 (no meaningful external equivalent): the 239c paired run EMPIRICALLY PROVED Kruze-new-tab and Chromium-new-tab are not equivalent primitives — Chromium’s Ctrl+T paints its full New Tab Page (Google logosearchshortcuts, a heavy web doc) while Kruze paints a light blank tab, so a ratio would compare different work. Raw same-methodsame-host numbers kept as context only: Kruze p50=141p99192 (n19 clean cycles, no crash), Chromium p50479/p99648 (n=20). Honest signal: Kruze’s new-tab primitive is fast (~141ms) AND stable across 19 clean Ctrl+TCtrl+W cycles — the 3rd converging confirmation (after the #239e load curve + crash finding) that the Owner’s “seconds” pain is NOT the new-tab act but webviewCEF contention under N tabs. R6 perf-debt obligation (acceptable tier → must open a fix ticket) is ALREADY DISCHARGED by kruze#240 (hard live-browser ceiling, DONE). Tier acceptable is the seed; owner verdict pending (R6 owner-override). The real-page degradation (heavy tabs UNDERESTIMATED by blank tabs here) + open-context-menu are tracked as the #239 residual, gated_by: evidence-soak on the #227 AV-seat. |
| — | product:productshorizontalkruze | open-context-menu | cef-desktop | linux-x11 | — | — | — | seed | seed | Chrome (CDP, version TBD) | — | kruze#239 residual — needs mouse input (Button3); cage seat only has wtype (keyboard), XTEST kills Kruze under sw-XWayland, no network for ydotool/uinput → blocked-by: kruze#227 (AV-seat with mouse). |