perf(chat-open): a chat shows its newest messages sooner

Closed
TE
4d ago
+229-67

HOLD: the fix on this branch (scroll-anchor read beside the connect) shows no gain in the bench; not landing until a change with a measured gain replaces it.

Leo-ask: ask-78e06958ab

Leo: "and i also want things to load faster"

What the harness says (bench/chat-open.bench.ts, Linux CI, production vite build, median of 5)

A cold chat open (every chat open in the app is a cold page: AgentPaneSpareReadiness.adoptsExistingChats = false, HQ#5914):

stage300 turns1,500 turns
DOMContentLoaded11 ms13 ms
socket opened156 ms157 ms
initialize arrived204 ms206 ms
attach answered205 ms207 ms
newest message painted330 ms327 ms
  • Chat length does not matter: the attach page is 400 events / 73 KiB either way.
  • Before the socket: script evaluation and the pane's first render (the bench's dev entry also loads every locale and its chunks one after another, so it overstates this part).
  • Socket open to initialize: 71-75 ms with an instant daemon. The page is busy rendering the handshake's chrome (composer, model picker, header) before it handles open.
  • Attach to newest message: ~125 ms of render. The first frame draws 16 rows, 84 rail entries, 711 elements; the profile spreads it over the placement effect's first forced layout, drawnHeightOf, the rail and the header's overflow measure, with no single hotspot.
  • The client already sends status, watch and the early attach in one burst after initialize. Sending the attach before initialize answers is not safe: acpmux takes the person proof, auth and compression from initialize.
  • Chat switch (chat-switch.bench.ts, 1,500 turns): 73-83 ms with an instant daemon, 67-108 ms at 40 ms per answer.

The change tried here, and its result

The open read the chat's kept scroll anchor from the host (chat.scrollAnchor.get) before it started the connect. The branch reads it beside the connect and holds the client's frames until it settles. A regression test proves the order (the attach goes out before the host answers, and the chat still opens at its kept place: 1 fail on base, 3 pass with the change). But the newest-message time did not move: the anchor read already overlapped the first render.

host + daemon latencybefore (ms)after (ms)
0477, 607529, 670
20610, 534530, 484
40553, 495609, 517

The bench tooling added here (stage rows, LATENCY, HOST_LATENCY, TRACE=1, profile windows) stays useful for the next attempt.

Next lever

The boot is paid on every chat open because a tab opening an existing chat never adopts the warm spare page (HQ#5914). Fixing that adoption removes the whole boot stage (about 150-200 ms of the 330 ms here) from every open.

Agent session: agent session

šŸ¤– Generated with Claude Code