For the two manual CI lanes, raw reports, fixtures and their coverage limits, see CI.md. Those lanes verify DOM events and consumer state; they do not use the legacy native-input helpers below.
Measuring what a session switch costs in the running Desktop app, over CDP. The findings in #4109 were produced with these; they live here so the next measurement is a command rather than a rebuild.
They are ad-hoc tools, not a benchmark suite: they attach to a dev app you started, and they answer “how much” only for the interaction they drive.
Start the dev app with the debugger open:
npm run dev -w @maka/desktop -- --remote-debugging-port=9334
Then, with a populated sidebar (about 30 rows is what the #4109 numbers used):
node scripts/perf/session-switch-commits.mjs before node scripts/perf/session-switch-busy-js.mjs
session-switch-commits.mjs — React commits per switch, how many are full-tree renders, and how many fibers actually re-rendered. Reloads the page first, so react-commit-probe.js is in place before React boots.session-switch-busy-js.mjs — renderer busy JS per switch, with the top self-time entries. It reports the running build, one configuration. A before/after needs both configurations in one instance; see below.react-commit-probe.js — the in-page half: a minimal __REACT_DEVTOOLS_GLOBAL_HOOK__ plus a Function.prototype.bind wrapper that catches React creating a dispatchSetState, so a commit can be attributed to the setState that caused it.cdp-client.mjs — the protocol client and the row-clicking helper.Never compare numbers from two app launches. Restarting the app shifts these metrics by orders of magnitude, while the spread inside a single running instance is small. An A/B means: one instance, a globalThis switch to select the configuration, at least three repetitions per configuration, alternating, compared pairwise. Two runs of “before” and “after” against two launches will produce a confident number that means nothing.
The switch is a throwaway, not a shipped affordance. Branch a worktree, put a globalThis flag in the one place that reads it — one that restores the old behaviour when set, so the regressed configuration is reachable without rebuilding the defect by hand — alternate the flag inside the running instance, then delete the worktree. Measurement discipline is ours; product code carries none of it. A flag that reaches main is a branch nothing executes and a defect replica that has to be maintained against the code it replicates.
Two smaller ones that cost time to rediscover:
Input.dispatchMouseEvent. A synthesised element.click() does not activate an Astryx SideNavItem, so the app does nothing and the measurement describes it.__MAKA_PROBE__.arm = false) before sampling. Its fiber walk otherwise shows up in the profile as the app's own work; session-switch-busy-js.mjs does this for you.The commit count is unweighted, and busy JS on this workload is spread across about a thousand cheap fibers rather than concentrated in one component. A change that lowers the commit count has not necessarily lowered the time, and a change that moves the top self-time entry has not necessarily lowered anything. Read both, and read the medians.