🤖 perf: let React Compiler compile ChatPane - #4427
Merged
Merged
Conversation
ThomasK33
added this pull request to stack #4430
September 24, 2026 11:31
ThomasK33
marked this pull request as ready for review
September 24, 2026 11:56
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ThomasK33
force-pushed
the
perf-compiler-chatpane
branch
from
September 24, 2026 12:42
e366c0c to
fd515ff
Compare
Member
Author
|
Perf evidence for this PR, from the
So this PR also removes the one real extra cost behind the 09-21 nightly step. If this PR stalls, the fallback is a small, compiler-safe fix that makes Generated with |
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 24, 2026
ThomasK33
force-pushed
the
perf-compiler-chatpane
branch
from
September 24, 2026 14:45
fd515ff to
b024f8f
Compare
yermakoffivan
pushed a commit
to yermakoffivan/mux
that referenced
this pull request
Sep 25, 2026
## Summary
The workspace-open perf scenarios now record in-page milestone times:
first transcript row ("useful content ready"), `data-loaded` ("fully
revealed"), and the longest main-thread task. They're written into
`perf-summary.json`, so these numbers no longer depend on when
Playwright's assertion polling happens to notice the change. Addresses
coder#4441.
## Background
The nightly `workspace-open-large` numbers stepped up on 09-21. The
investigation in coder#4441 showed that was mostly a change in what the spec
measures, not extra work:
- coder#4293 moved the `data-loaded` milestone later.
- `toHaveAttribute` polls with a +100/+250/+500/+1000 ms backoff, so the
gap between the real flip and the passing poll went from ~27 ms to ~423
ms.
The small real leftover was fixed by coder#4427.
## Implementation
- `tests/e2e/utils/pageMilestones.ts`:
- A MutationObserver records the first `chat-message` row inside the
message window and the `data-loaded="true"` flip. Its callbacks run in
the same task as the DOM change, so there is no polling quantization.
- A `longtask` PerformanceObserver records the longest task, and pending
entries are drained on read.
- It asserts it wasn't started twice, the window isn't already loaded at
start (otherwise the milestone would read 0), and the first message
never comes after full load.
- `writePerfArtifacts` takes an optional `milestones` field, written as
a top-level key. The change is additive, so `schemaVersion` stays 1.
- The workspace-open spec starts the milestones right before opening the
workspace and asserts that `fullyLoadedMs` was captured.
- Out of scope: versioning the measurement contract (so a trend baseline
resets when a milestone changes meaning) belongs with the deferred trend
reporter in coder#4442.
## Validation
Local, dist build: `perf.workspaceOpen.spec.ts --repeat-each=3`, 18/18
passed. Medians in ms:
| Scenario | First message | Fully loaded | Longest task | Playwright
wall | Wall minus loaded |
|---|---|---|---|---|---|
| large | 806 | 1448 | 274 | 1547 | 171 |
| medium | 772 | 907 | 184 | 1016 | 123 |
| tool-heavy | 772 | 884 | 131 | 1063 | 179 |
| reasoning-heavy | 792 | 891 | 112 | 1091 | 195 |
| large-diff | 1036 | 1089 | 101 | 1103 | 12 |
| small (first test in the worker, cold) | 1135 | 1135 | 170 | 1189 | 54
|
The last column is the polling overhead that the old wall-clock number
included.
## Risks
Test-only. The observer adds one or two `querySelector` calls per
mutation batch inside the profiled window, until both milestones are
seen. That's negligible next to the ~800 ms of script time.
---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ChatPaneContentnow compiles under React Compiler and leaves the guard baseline.Implementation
handleSendQueuedImmediatelythrew insidetry/catch(unsupported). The interrupt call now goes throughrunWithCatch, which releases the in-flight guard and rethrows on error. The failure-result path clears the guard and throws outside anytry, so the guard is cleared once instead of twice (clearing is idempotent).handleEditQueuedMessageandhandleSendQueuedImmediatelydrop theiruseCallbackwrappers. Their optional-chaining deps (workspaceState?.queuedMessage) did not match what the compiler inferred, so it refused to compile the component. The compiler memoizes both now.Validation
11/18 hot components compile (7 known skipped).tests/ui/chatpass.Measurements
Electron perf e2e (
tests/e2e/scenarios/perf.*.spec.ts,XUM_PROFILE_REACT=1), local, serial, 3 runs each, medians. Baseline ismainat 349657d; "after" is the top of this stack (#4429), because the layers were measured together. React time is the summedactualDurationof the profiled subtrees.chat-pane.transcriptchat-pane.inputchat-pane.transcriptchat-pane.inputchat-pane.transcriptCommit counts are unchanged; the gain is less work per commit. Typing in a large chat and the review hunk specs show no change beyond noise. ChatInput alone, measured before #4418 landed: New Workspace typing went from 165
CreationControlsrenders per run to 2.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$4.05