evidence · 18.8 KiB

README.md

Committed at docs/evidence/theme-picker/board-session-2026-10-12b/README.md. This is what grounds the requirements that cite it.

Open raw source

Board session 2026-10-12 (b): memory, motion curves, warm tap-apply

Tasks 11.3, 18.3 and 6.6 of change the-shell-swaps-themes-without-a-python-stall.

The operator asked for this session, without being present: “just test stuff that doesn’t need me as far as you can then lmk what you need”. The work follows the first board session of the same day (../board-session-2026-10-12/README.md) and its tools, workloads and gates.

The source is branch perf/theme-row-memory-trace, in worktree /home/jadams/tmp/k230-row-memtrace. Its base is master 6faf4b9b, and it merges perf/theme-tap-apply. Times are UTC unless marked local (board local time is CDT, UTC-5).

Evidence classes:

No finger or camera observation is claimed. Raw captures stay private in ~/tmp/k230-session-b-run/ and are not committed.

Builds

Four candidates were built, each with flock /tmp/k230-nix-build.lock nix build .#kernelMainlineDrmShellBootFiles --out-link ~/.local/state/tdisplay-k230/retained-builds/row-memtrace-2026-10-12/<name> --print-out-paths --max-jobs 1 --cores 4. Each was swapped in through a /run drop-in (wrapper copy with the final exec replaced) for its board runs.

All four candidate ELFs have the same runtime reference set as baseline 8cik (md5 adb9f783…).

CandidateCommitRust ELFWhat it added
10cfbb9e6x6hhs64w…Rest-size sprite cache only, per-frame carousel trace record, tap without a preview, pressed-wash fingerprint
23e9df8d5zfck2cyv…LRU card cache (4 MiB), reused scratch surfaces for in-between cards, release and malloc_trim when a scene has no picker
3b01b580da7aid2j6…Diagnostic: logs the pre-render’s content parts beside the current ones on a mismatch
4e93b5a233m65ag9z…Fingerprint recomputed on every view update, only collapsed slices cached (2 MiB), trim and picker-memory log at gesture rest

Candidate 4 is the measured and installed build. The tables below are all candidate 4 unless named otherwise.

Task 11.3: matched pairs against the repaired baseline

Each run used tools/go.sh and tools/seq.sh, the same run.py / 13.1 workload as the first session, recorder off.

The order was:

  1. p1 cand, base (16:56);
  2. p2 base, cand;
  3. p3 cand, base.

Every run passed the profile harness gates, and every compare reports comparable: true (11.3/compare-of-p1.json and the next two). The reduced reports are in 11.3/reports/, and the pair table is 11.3/pairs-summary.json.

The baseline is 8cik (repaired baseline, same as the first session). The candidate is 3m65ag9z.

Metric (median of three pairs)BaselineCandidatePer-pair delta
Warm theme commit gap, median ms14063-81, -79, -65.5
Warm theme commit gap, p95 ms17389-84, -99, -65
Cold theme commit gap, median ms14467-78, -63, -67
Background commit gap, median ms8438-50, -46, -32.5
Warm theme commits5997+39, +40, +29
Rust CPU %, warm theme swipes79.057.0-21.1, -21.7, -22.1
Rust CPU %, background swipes46.09.2-36.8, -37.5, -15.8
Rust CPU %, idle0.480.51-0.48, +0.01, +0.06
Rust RSS at end of warm idle, MiB82.584.5+2.0, -2.3, +11.0
Rust RSS peak, MiB82.587.4+4.9, +3.9, +13.2
Helper RSS, MiB24.023.4-0.7, +0.2, -0.7
Theme preview requests515+9, +7, +12
Overlay pre-renders completed (none with a touch held)48+3, +1, +11

Memory attribution. Per-phase Rust RSS shows the following (11.3/reports/, units.shell-ui.service.rss_last_bytes):

While the picker is open, the shell’s own picker-memory log at gesture rest reports (6.6/journal-trial-1.txt):

ItemSize
Card sprites1.96–2.03 MiB
Outline masks52 KiB
Scratch surfaces1.5 MiB
Thumbnails (same bounded cache as the baseline)7.2–8.1 MiB
glibc heap in use22–25 MiB
Free after trim4–6.8 MiB

The candidate’s +2 MiB median is therefore the picker’s deliberate 3.5 MiB sprite and scratch working set, released when the picker closes.

How the build got there:

The candidate makes three times as many speculative preview requests as the baseline. Helper RSS is unchanged.

The shell now writes one carousel trace event per committed frame on the Settings route:

Source: Carousel::trace_state in nix/rust-shell-client/src/theme_carousel.rs and the commit site in main.rs.

Runs g-theme-1, g-bg-1, g-theme-2, g-bg-2 (16:43 to 16:56) used tools/gog.sh. That is the first session’s run.py --workload gestures with the five injected programs (flick, slow, hold-release, reversal, interrupt), two repetitions each, a fresh Settings to Themes opening per gesture, and trace mode. Generation and process identities were checked after every gesture and never changed. No trace dropped an event.

tools/curves.py plots every committed frame. Nothing is replayed through a host model. It anchors “release” on the record’s own last finger-phase frame, because the shell handles an injected release 6–130 ms after it is written.

Outputs:

Gesture (median of 4)RowFinal itemItems after releaseOvershoot past finalRelease to rest, msFrames after release
flicktheme10 (4 of 4)8.08083712
slowtheme31.16048210.5
hold-releasetheme10.0603708
reversaltheme0-0.29041812.5
interrupttheme7-0.69063211
flickbackground3 (the last item)1.62043914
slowbackground30.26030510
hold-releasebackground1-0.39034210
reversalbackground0-0.29041913
interruptbackground2-0.90061717.5

Observations only:

Whether the travel, settling and interruption feel right is task 18.4’s finger question. These curves do not answer it.

Task 6.6: warm tap-apply

tools/apply-swap.sh ran the first session’s apply trial (tools/apply_trial.sh) twice on candidate 4 through the /run drop-in. Each trial does the following:

  1. Cold restart of theme-helper and shell-ui.
  2. Settings to Themes, then wait 15 s.
  3. Hold-release to the light neighbour, then an injected centre tap.
  4. Swipe back to the dark item, then an injected centre tap.

Trial 1 started 16:41:06 and trial 2 at 16:42 (local 11:41 / 11:42). Sanitized journal excerpts: 6.6/journal-trial-1.txt and 6.6/journal-trial-2.txt, via tools/sanitize_journal.py. The markers are in 6.6/marks.txt.

Light (t1 / t2)Dark (t1 / t2)
optimistic-apply shown ms181.7 / 189.4153.4 / 150.5
prerenderedtrue / truetrue / true
stage adopt / wallpaper / overlay ms16.0 / 4.6 / 54.8; 12.4 / 7.5 / 56.514.9 / 21.4 / 14.3; 15.3 / 19.3 / 11.0
Tap-up to shown frame (journal Rust clock), ms185 / 190154 / 151
activate_generation total, ms; prepare_phase ms; warm173.8 / 174.2; 0.2 / 2.8; 2140.1 / 140.8; 2.7 / 2.6; 2
theme-command activate round trip, ms323 / 326288 / 280

The first session’s figures were:

In this session:

The cold-open neighbour check passed in the first session and was not repeated.

Follow-up: what the tap waited on

Same trial, same recipe (tools/apply-swap.sh with the candidate path changed), two cold-process trials per candidate, injected taps. Candidates 5–8 add the entry_ms/checks_ms stage fields; source is the perf/theme-row-memory-trace branch after 89f8b25e.

  1. Candidate 5 (7rfw6yc2, timing only). entry_ms, from the Activate submit to the optimistic check, was 106–193 ms of the 140–235 ms shown time. The touch-up handler ends with a synchronous picker draw (the pending and pressed state), which ran before the loop reached the optimistic check. Journals are in the private run directory only.
  2. Candidate 6 (2jgydicg). The touch-up handler and the overlay frame callback skip that draw while a fresh Activate awaits the optimistic check; the loop draws anything still dirty afterwards. Tap-up to shown frame: 27.8–31.1 ms, entry_ms 3.4–6.1, prerendered=true 4 of 4 (6.6/candidate-6/). The helper’s commit then waited longer: the shell spent 336 ms repainting the hidden Home layer (K230_HOME_FRAME ms=336) before acknowledging. Shell commit exchange 165–232 ms, commit_phase 215–238 ms, activate round trip 459–476 ms.
  3. Candidate 7 (bgdk0j87). Defers the Home repaint while an optimistic frame awaits its commit (cap 2 s). The commit event clears that state on arrival, and the loop still repainted Home before reaching the deferred ack: shell commit exchange 214–232 ms, commit_phase 281–301 ms. Private run directory only.
  4. Candidate 8 (hl4bca4g). Also acknowledges a reused optimistic commit as soon as the event arrives, before that tick’s Home repaint (6.6/candidate-8/):
Candidate 8Light (t1 / t2)Dark (t1 / t2)
optimistic-apply shown ms27.5 / 28.829.2 / 34.2
prerenderedtrue / truetrue / true
shell commit exchange, ms48.1 / 52.637.2 / 42.9
activate_generation total; commit_phase, ms179.1 / 174.9; 110.5 / 115.9166.5 / 170.6; 105.1 / 105.7
theme-command activate round trip, ms338 / 339333 / 342

Home repaints 350 ms after the ack (K230_HOME_FRAME ms=347–355), off the commit path. Every apply changed the active theme link (ACTIVE_* in 6.6/candidate-8/marks.txt). Native grim captures of the picker after each apply (trial 2 of candidates 6 and 8, viewed, not committed) show the applied theme’s chrome and “Current theme” on the applied item.

The visible apply is now well under 150 ms. The durable activation, measured by the helper, is not: activate_generation is 167–179 ms, and prepare_entry (82–101 ms) plus commit_phase put the round trip at 333–342 ms. The commit exchange with the card process (32–35 ms) and swap_pointer (23–39 ms) remain inside commit_phase. Neither the injected taps nor these journals are a finger-on-glass measurement.

Install

IdentityValue
Bundle/nix/store/1dkw87zfhgfygnmv9qxdrbcqy2xqzxfg-k230-coherent-shell-boot-files
System/nix/store/fm8gk6xica4h7ni3fp6wgya3lmfxfzjr-nixos-system-nixos-26.11.20260919.20b1ddd
Rust shell ELF/nix/store/3m65ag9zlzffdd86ajjfq7si96cj46y1-k230-shell-rust-riscv64-unknown-linux-gnu-0.1.0/bin/k230-shell-rust
Kernel, Image, initrd.uimgunchanged from the baseline
Baselinesprite install rav09qp6… (../row-sprites-2026-10-12/)

The coherent installer ran with the row-sprites recipes. Only the paths, the baseline and the saved catppuccin appearance differ (install/expected-runtime.json; see install/*-used.py). Every step passed:

  1. Preflight: boot 49fb3850-…, 17:07:32 (install/preflight.json).
  2. Stage: (install/stage.log, install/transfer.json, install/staged-state.json, install/host-inspection.json).
  3. Candidate boot: (install/candidate-boot.json). The runtime check ran on boot dd17a5e9-… at 17:12:19 (install/candidate-runtime.json).
  4. Qualification: (install/deployment-authorization.json).
  5. Install and ordinary autoboot: (install/ordinary-boot.json). The runtime check ran on boot 34d77814-… at 17:15:31 (install/ordinary-runtime.json). The current system and profile are both fm8gk6xi…, the four shell services are active, and the saved appearance is unchanged.

install/installed-picker-native.png is a native grim -s 0.5 capture taken on that ordinary boot at about 17:16. Settings was opened and Themes was tapped by injection. It shows catppuccin as the current theme, its neighbours as side slices, and the Backgrounds row loaded. No finger observation was made on the installed boot.

Install of candidate 8

IdentityValue
Bundle/nix/store/amvl5qymc5vgq3b9y1r659c9lf96gyzx-k230-coherent-shell-boot-files
System/nix/store/298a2bf6sqpc82ngdryxgg3xmr9fg9f7-nixos-system-nixos-26.11.20260919.20b1ddd
Rust shell ELF/nix/store/hl4bca4gi4m0wmhwas1i1zl8n360m744-k230-shell-rust-riscv64-unknown-linux-gnu-0.1.0/bin/k230-shell-rust
Theme helper/nix/store/2m37rx16riv3mjxvajfvjakncs9gszkb-handheld-theme-command-0.1 (changed only because it names the Rust ELF)
Source56a7b94e
Kernel, Image, initrd.uimgunchanged
Baselinecandidate 4 install fm8gk6xi… (above)

The same installer recipes ran from a new run directory with the paths, baseline, bundle already on the board, and saved appearance changed (install-2/, *-used.py, install-2/expected-runtime.json). Every step passed:

  1. Preflight: boot 34d77814-…, 18:35:00 UTC (install-2/preflight.json).
  2. Stage: (install-2/stage.log, install-2/transfer.json, install-2/staged-state.json, install-2/host-inspection.json).
  3. Candidate boot: (install-2/candidate-boot.json); runtime check on boot c9d1793e-… at 18:40:10 UTC (install-2/candidate-runtime.json).
  4. Qualification: (install-2/deployment-authorization.json).
  5. Install and ordinary autoboot: (install-2/ordinary-boot.json); runtime check on boot 94fd02a1-… at 18:43:21 UTC (install-2/ordinary-runtime.json).

On that ordinary boot the apply trial (tools/apply_trial.sh, no drop-in, injected taps) ran once (install-2/journal-installed-apply.txt, install-2/marks-installed-apply.txt):

InstalledLightDark
optimistic-apply shown ms28.042.2
prerenderedtruetrue
activate_generation total; commit_phase, ms152.0; 91.3148.7; 85.7
theme-command activate round trip, ms313309

A floating terminal covered Home during the trial, so Home drew no frame while covered. Moving the terminal to the sway scratchpad drew one (K230_HOME_FRAME ms=203), and install-2/installed-home-after-apply-native.png (native grim -s 0.5, about 18:51 UTC) shows Home in the applied dark catppuccin theme. The terminal was then shown again. No finger observation was made on the installed boot.

Board state afterwards