Skip to content

🤖 perf: desktop cold-start A/B runner and dispatch job - #5997

Merged
ThomasK33 merged 7 commits into
mainfrom
perf/t3-pr0b-desktop-cold-start
Oct 10, 2026
Merged

ThomasK33 merged 7 commits into
mainfrom
perf/t3-pr0b-desktop-cold-start

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Adds the desktop cold-start A/B runner for the first-load JS plan (T3, Refs #5971) and a workflow_dispatch job that runs it on CI. It measures the xum:app-shell-ready mark from #5981 in the Electron renderer. No src/ change and no user-visible change.

Background

The T3 code-splitting PRs must show that they do not slow down Electron startup. The plan's desktop protocol (D2) uses the mark's startTime, measured on cold launches with base and head interleaved. This dev host has no display server, so the measurement of record runs on a CI runner under xvfb-run.

Implementation

  • scripts/perf/desktopColdStartStats.ts (pure): launchPlan alternates 2 warm-ups per arm, then runs pairs in ABBA order (base-head, head-base, ...) so order effects cancel. pairedRelativeDelta returns the mean per-pair relative delta (head − base)/base, its sd, and the one-sided 95% half-width and upper bound. It uses a one-sided t table (t = 1.729 at 19 df), not the two-sided table in benchStats.ts.
  • scripts/perf/desktopColdStart.ts launches each built tree's own Electron the same way the load-dist e2e harness does (XUM_E2E=1 XUM_E2E_LOAD_DIST=1, mock AI). Before every launch it copies a seeded root fresh to one fixed path, because config.json holds absolute paths. It reads exactly one mark per launch and fails the run on any launch failure, with no retries. It prints a per-launch table and the summary, or --json.
  • The runner runs under Node (node --import tsx), not Bun. Under Bun 1.3.12, Playwright's _electron.launch never finishes the websocket upgrade to Electron's debugger, so every launch hangs. Remote UAT showed this. bun x playwright test also runs under Node.
  • Only the result goes to stdout. Electron's lazy binary download logs to stdout, so the script sends console.log to stderr, which keeps --json output valid.
  • .github/workflows/desktop-cold-start.yml: dispatch only (inputs base_sha, head_sha, pairs). Permissions are contents: read, checkout uses persist-credentials: false, and the action SHAs are copied from perf-profiles.yml. Inputs reach shell steps only through env: and are checked with a regex. The job builds head at the workspace root and base in a git worktree, runs the runner under xvfb-run -a, and uploads cold-start.json. The runner comes from the head tree, so head_sha must contain this PR.

Validation

  • GitHub can dispatch a workflow only once it is on the default branch, so this job cannot run before merge. After merge I will dispatch one same-SHA A/A run (base = head = main, 20 pairs) and post its mean delta and half-width on perf: xum server first load downloads and parses about 9.8 MB of JavaScript #5971. If the half-width is at most 2.5%, D2 gates later PRs. If it is wider, D2 is information and D1 gates.
  • Local run on this host at c048aa4 (the two later commits change only cleanup and the entry guard), under a private virtual display, with a built tree in both arms and 3 pairs. The text and --json runs both exited 0. Both runs made 10 launches in the order bw hw bw hw b0 h0 h1 b1 b2 h2 (b/h = base/head arm, w = warm-up, digit = pair index). A recompute matched stats, and no temp dir was left behind. --pairs 1 exits 2.
index	arm	pair	mark ms	wall ms
0	base	warmup	1487.9	3282
1	head	warmup	1364.6	3064
2	base	warmup	1520.1	3231
3	head	warmup	1413.3	3142
4	base	0	1386.2	3081
5	head	0	1376.4	3128
6	head	1	1418.2	3146
7	base	1	1378.2	3102
8	base	2	1442.9	3157
9	head	2	1466.9	3167

n=3  mean delta 1.29%  one-sided 95% upper bound 4.38%
half-width 3.09% (<= 2.5%: no)
median mark ms: base 1386.2, head 1418.2
  • Remote UAT on Coder Agents (3 rounds, one chat). Round 1 found the Bun hang. Round 2 found a temp-dir leak on an uncaught Playwright timeout. Round 3 passed on 5753377: --pairs 3 --json gives 10 launches in ABBA order with stats that match a recompute. Eight --timeout-ms runs from 1 to 200 ms each exit 1 in about 1 s, with no temp dir or Electron process left. Bad input exits 2. A run with two trees whose Electron binary must first be downloaded keeps stdout valid JSON.
  • Known Low follow-ups (not fixed here, they need more than the 300-line budget allows): Playwright's own uncaught timeout stack names no launch index (stderr progress lines show the last finished launch). SIGTERM to the node process alone does not stop a run. tsx comes in only transitively (through e2e), not as a direct dependency. The base tree's Electron can download before a bad head tree is rejected.
  • make lint-actions (actionlint and zizmor) and make static-check pass. check-react-compiler still prints 23/24 hot components compile (1 known skipped).

Risks

None for the product. The workflow runs only on manual dispatch, with read-only permissions.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T01:14:25.463199Z 5753377 PR opened
🔒 Security Review ✅ Completed 2026-10-10T01:20:21.376232Z 5753377 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit d0a0021 Oct 10, 2026
31 checks passed
@ThomasK33
ThomasK33 deleted the perf/t3-pr0b-desktop-cold-start branch October 10, 2026 01:36
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 10, 2026
## Summary

The desktop cold-start job from coder#5997 uploads a `cold-start.json` that
is not valid JSON. This PR moves the stdout redirect inside `sh -c`, so
only the runner's JSON reaches the file. Refs coder#5971. No product change.

## Background

The first A/A dispatch (run
https://github.com/coder/xum/actions/runs/38013815349, base = head =
`d0a00215b0`, 20 pairs) succeeded, but its artifact starts with 46 lines
that are not JSON: 44 runner progress lines and 2 `Downloading Electron
binary...` lines, followed by the JSON document. Ubuntu 22.04's
`xvfb-run` runs its command with `"$@" 2>&1` (line 184 of
`debian/local/xvfb-run` on `ubuntu/jammy-updates`), so the runner's
stderr went into the redirected stdout. Current Debian removed the
`2>&1`, which is why this was easy to miss.

## Implementation

`xvfb-run -a sh -c 'node ... --json > cold-start.json'`: the inner shell
redirects only the runner's own stdout, so stderr (progress lines) now
goes to the job log. The variables still reach the step only through
`env:` and are validated by the existing regex step. A scoped `#
shellcheck disable=SC2016` marks the single quotes as intended.

## Validation

- I ran the exact step text under a stand-in `xvfb-run` that does `"$@"
2>&1`, with a stub `node`. The old form gives a file that does not
parse. The new form gives a file that `jq -e .` parses whole, and the
stub's stderr still shows on the console.
- `make lint-actions` (actionlint and zizmor) and `make static-check`
pass.
- After merge I will dispatch the A/A again and check that the whole
uploaded file parses.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant