Repository navigation
perf cron: web UI perf harness on Linux (web-ui part, PERF_ROW lines) - #31704
Conversation
…f harness)
The web UI benches each printed only their own human text, and only diff-stream ran on CI. Now:
- webviews/bench/perfRow.ts: a machine-readable `PERF_ROW {...}` line (name, p50, p95, unit, target,
floor, version) beside each bench's human output, with one target rule per kind of row (response
100 ms, cold load 1000 ms, frame work 10 ms).
- webviews/bench/web-ui-suite.ts: runs chat-open, chat-switch, transcript-scroll-frame,
thread-rail-jump, transcript-view-rows, edit-turns-stream and agent-activity-groups with short
args, collects their rows, prints them slowest first relative to target, and lists every bench
that exited non-zero, timed out or printed no row.
- perf-cron.py: a second Linux part "web-ui" (rows webui.*) through the same compare, confirm,
bisect and issue machinery as diff-viewer; a failed bench makes the part incomplete; the step
summary lists the rows slowest first. diff-viewer's config hash is unchanged.
- The cron's linux job installs Rust only for diff-viewer and Playwright's chromium and webkit only
for web-ui; same runner (the plan's linux_runner), still advisory and cron-only.
Leo-ask: ask-8bc0f906d8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0113219d38905033a039
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: Shipped in:
|
|
Found 12 test failures on Blacksmith runners: Failures
...and 2 more test failures. View all on Blacksmith
|
…target A headless runner's frame clock sets it (16.7 ms on the first Linux run), so it is not one frame's work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0113219d38905033a039
|
Review (codex, router-review): SHIP at 5ff9e0f Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 1. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at 5ff9e0f Visual verdict: no images; Leo tries the build The cron and CI tests pass ( Follow-ups:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-10-28: "and we want TONS of perf stuff... when it comes to the left scroll rail thing, i already said we want it ultra fast ultra responsive, but the same goes for EVERYTHING IN THE WEB UI"
Leo-ask: ask-8bc0f906d8
What
A shared web UI perf harness that runs in the existing perf cron (
cmux-next-perf-budgets.yml+scripts/cmux-next/perf-cron.py) as a second Linux part, next todiff-viewer. It is advisory and cron-only. It adds nothing to the push, pull request or nightly path (Lawrence 2026-10-23, see ci-web.yml), adds no new job and no newruns-on: it reuses thelinuxjob and the plan'slinux_runner. No check fails on a regression. A regression goes to the one "cmux-next perf regression" issue through the same compare, confirm, bisect and issue logic the diff rows already use.The repo CLAUDE.md says not to add CI gates, perf rows or CI steps. This adds no gate. Lawrence named the perf cron as the place for measurements ("we can have one running on a cron job"), so these rows report there, and nothing waits on them. Leo owns CI and asked for this harness (rulebook: "Lanes measure interactions with a shared web UI perf harness on Linux CI and fix the slowest first").
webviews/bench/perfRow.ts: a machine-readable line beside each bench's existing human output:PERF_ROW {"name":"chat_switch.unread.latest","p50":..,"p95":..,"unit":"ms","target":100}.Targets follow one rule per kind of row, never today's number: a response (click, switch, until painted) is 100 ms, a cold load until the newest message is painted is 1000 ms, and main-thread work per frame (scroll, stream, per-token reads) is 10 ms. Row names never end in
.frame,.missedor.dropped, so the cron does not judge them by display refresh (a headless runner has none).webviews/bench/web-ui-suite.ts: runs the benches below with short, CI-sized args, collects their rows, prints them slowest first relative to target, and prints one JSON report as its last line. Any bench that exits non-zero, times out or prints no row is listed infailed, so nothing passes silently.--only a,band--out FILEare there for local runs.perf-cron.py: adds the partweb-ui(where: linux, rowswebui.*,needs: web-ui).supported_parts_atmeasures only commits that haveweb-ui-suite.ts.rows_of_partkeeps the bench's target as the row's budget, andtarget_ofreads it (no target means none, not the 10 ms latency default). The floor is the bench's own, or 10 % of p95. Each row carries itsversion, so bumping it rebases the row. If one bench fails, the part isincomplete: its rows still count, but the run is not a last-good baseline. A run with no rows fails.run-linux-part --part web-uiprints a Markdown table, slowest first against target, into the step summary, so a lane can "fix the slowest first".run-linux-partdefaults to--part diff-viewer. diff-viewer'spart_confighash is unchanged (9c9749c7fe76, asserted in a test), so its rows are not rebased.linuxjob: Rust is installed only fordiff-viewer.playwright install --with-deps --only-shell chromium webkitruns only forweb-ui. The step passes--part "$PART".Benches
chat_open.compact.{attach,first_row,latest,settled,long_ms}chat_switch.{unread,read_ahead}.{latest,settled,long_ms}transcript_scroll.{fling,wheel}.{react,layout}_msthread_rail.{tick_click,tick_reveal,scroll}.loaded_1000.basetranscript_rows.stream.setedit_turns.{full,tracked}.{1000,5000,20000}agent_activity.groups.{snapshot,local}.{100,1000,5000}Excluded:
+canvasarm, so the suite would read that as a failed run. thread-rail-jump already covers the rail's frame cost.transcript-scroll-frame's frame intervals are not rows, because a headless runner's frame clock is not a display's. Its React and layout work per frame are rows.
Verification
python3 -I scripts/cmux-next/tests/test_perf_cron.py:Ran 176 tests ... OK. There is a newWebUiTestsclass: rows, targets, floors, versions, regression detection, incomplete on a failed bench, slowest-first summary, andrun-linux-part --part web-uiwith a faked suite (done plusincomplete, or failed when there are no rows). Two existing tests were updated, because the Mac-disabled path now runs both Linux parts.python3 -I scripts/cmux-next/tests/test_perf_cron_io.py:Ran 27 tests ... OK.tests/test_ci_linux_single_size.py,test_ci_no_github_token_polling.py,test_ci_overflow_pick_reads_pool.py,test_ci_guard_workflow_structure.py: pass.cmux-ci test --setup webviews, fast-checks run 41382740922 at 5ff9e0f, 8 vCPU, Playwright's Chromium and WebKit):suite exit 0, all 7 benches ran, 31 rows,failed [], 119 s in total (chat-open 19 s, chat-switch 28 s, transcript-scroll-frame 51 s). The top of its slowest-first table:An earlier run (41381894015, at 5dff1bc) gave the same order: chat_switch 116-123 ms, thread_rail tick_click 63.7 ms, chat_open latest 430.7 ms. It also showed
thread_rail.scrollat exactly 16.7 ms, the headless frame interval. The second commit takes that row's frame-work target off (the row stays, so a dropped frame still shows).So the slowest web UI paths today are React work per frame during the transcript fling (66 ms p95 on that runner) and chat switch (about 120 ms, either arm). These are the first things to fix.
Observed / Not run
linuxjob with--part web-ui, then report). The first tick on feat-cmux-next after the merge is that end-to-end run. The runner class there is the plan'slinux_runner(4 or 8 vCPU Blacksmith), so its numbers will differ from the 8 vCPU fast-checks numbers above.Changelog
none
Agent session: agent session
🤖 Generated with Claude Code