Skip to content

🤖 perf: defer workspace-open git/PR probes until the chat replay settles - #4675

Merged
ThomasK33 merged 3 commits into
mainfrom
perf/defer-open-git-calls
Sep 26, 2026
Merged

ThomasK33 merged 3 commits into
mainfrom
perf/defer-open-git-calls

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Opening a workspace no longer starts its git status, git fetch, gh pr view and gh stack view probes while the chat history replay is still running. The probes now start when the chat reaches caught-up. On cold opens, the first transcript row appears ~70-95 ms sooner, and the server replay is 2-3x faster.

Refs #4662

Background

Opening a workspace mounts the footer, branch selector and PR links. Their store subscriptions queued an immediate refresh, which sent four workspace.executeBash calls ~5 ms before the onChat replay began. Each child-process spawn blocks the Electron main process for ~8-16 ms, so the replay's history read ran on a stalled event loop (found while diagnosing #4624; #4670 fixed the untrusted-project part of that measurement).

Implementation

  • WorkspaceStore.isWorkspaceChatReplayPending(id) is true from the moment a workspace becomes the active onChat subscription until one of these happens:
    • its caught-up arrives (complete or failed replay);
    • an attempt ends without caught-up (transport error or the 10 s stall watchdog). This is the fallback, so the probes never wait forever on retries;
    • the workspace is deactivated or removed.
      Each of these paths notifies subscribeKey listeners. No timers are involved.
  • GitStatusStore and PRStatusStore accept that gate via setChatReplayGate, wired once in AppLoader.
    • Their refresh loops skip a workspace while its replay is pending. This covers every trigger: subscribe, sync, focus and invalidate.
    • For each skipped workspace, one watcher (deferWhileChatReplayPending) calls requestImmediate() when the gate opens.
    • Other workspaces refresh as before.
  • Cached git status and the persisted PR badge stay visible while a refresh is deferred.
  • Added delay: probes now start at caught-up, ~85-115 ms after the store switch on cold open (median) and ~70-180 ms on switch-back. Switch-back already shows the cached status.

Validation

perf.chatSwitch (--workers 1), 4 alternating base/after runs on the same host, 12 switches per column per build. Medians, in ms:

metric cold-open-small cold-open-large switch-back-small switch-back-large
renderer.firstRowMs (store switch) 246.9 → 153.1 286.5 → 216.2 25.9 → 28.4 27.1 → 25.3
renderer.caughtUpMs 183.8 → 86.3 173.7 → 114.3 75.3 → 67.3 220.6 → 182.0
caught-up → first row (difference of medians) 63.1 → 66.8 112.8 → 101.9 – –
dom.firstRowFromClickMs 269.2 → 173.1 291.3 → 223.0 97.3 → 96.8 124.7 → 122.2
dom.longestTaskMs 69 → 75 104 → 107 98.5 → 98 127 → 129.5
server.totalMs 110.8 → 38.0 135.0 → 81.4 39.1 → 9.6 88.4 → 37.4
server.historyReadMs 87.6 → 28.1 94.2 → 50.1 26.7 → 3.1 41.2 → 24.8

The renderer's longest task did not change. In these baseline runs the probe results did not land during the paint (base longest task 69/104 ms, not the 146 ms seen in #4624), so this PR does not claim a longest-task improvement. The numbers were measured at the first commit; the later commits are a behavior-preserving refactor and a test-only rename.

Remote dogfood UAT (Coder Agents, browser mode, real git repo with a GitHub PR) passed:

  • On the switch path, the four probes started 155-204 ms after caught-up on this branch; on base they started 36-88 ms before it.
  • Footer git status and the PR badge still appeared and stayed correct across switch-back, file changes and restart.
  • The three issues it found are pre-existing or by design (they reproduce on base).

Risks

Low. This only changes when the passive git/PR refreshes of the active workspace run. They are late by at most the replay duration, or by one failed attempt (bounded by the stall watchdog) if caught-up never arrives. With no gate wired (tests, stories), behavior is unchanged. The untrusted-project no-repo git env check (#4661) is untouched.


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

…eplay settles

Opening a workspace mounted footer/link components whose git status, git
fetch, gh pr view and gh stack view probes spawned ~5 ms before the onChat
history replay, blocking the Electron main process and rendering results
during the transcript paint (#4662).

WorkspaceStore now reports whether the active workspace's first replay is
still pending (cleared on caught-up, on an attempt that ends without
caught-up, and on deactivation/removal). GitStatusStore and PRStatusStore
skip pending workspaces in their refresh loops and arm a one-shot watcher
that requests an immediate refresh when the gate opens. Cached status
stays visible during the deferral.

Signed-off-by: Thomas Kosiewski <tk@coder.com>
… stores

Move the per-workspace retry-map bookkeeping into deferWhileChatReplayPending so
GitStatusStore and PRStatusStore no longer duplicate it, and pass the
workspaceStore arrow methods to the gate directly. Behavior-neutral.
…th probes

bun does not interpolate booleans into %s, so both it.each cases shared one
title. Use string cases and check that the deferred refresh issues gh pr view
as well as gh stack view.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 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-09-26T12:13:48.367008Z 5c9ff89 PR opened
🔒 Security Review ✅ Completed 2026-09-26T12:15:50.338112Z 5c9ff89 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 Sep 26, 2026
Merged via the queue into main with commit 7881ab8 Sep 26, 2026
31 checks passed
@ThomasK33
ThomasK33 deleted the perf/defer-open-git-calls branch September 26, 2026 12:32
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