Repository navigation
fix(native-chat): bound desktop catch-up to newest page - #28951
brennanb2025 wants to merge 11 commits into
Conversation
…25/nc-perf-catchup
…25/nc-perf-catchup
Current status: real signed-in agent QA pendingThe source review, measurements and exact-head checks are complete. Both replacements remain drafts while the required real signed-in agent pass is pending. The M4Air attempt stopped before launch because the machine exceeded its permitted load; a newly authorized local QA route is being prepared under the same isolation and process-ownership rules. Earlier Electron runs below used a deterministic provider fixture through the real app and remain supporting evidence. No merge is authorized. The original catch-up change could remove a pending child approval or question when its conversation row left the loaded page. Review split the fix into #29121, which keeps complete current decisions independent of paged history, followed by #29125, which loads the newest page after a large hidden gap while preserving short-gap and older-client replay. Three source rounds and the final architectural decision are complete. Both replacement heads have green full PR workflows, exact-source hidden Electron comparisons with before/after screenshots in their PR bodies, final-stack measurements on the original Linux host, and passing actual paired bridge transport proof. The bridge run delivered over 5 MiB through bounded sequential replies with event acknowledgments withheld, preserved the complete request payload, and passed capability simulation, replay, reconnect and disposal. Native phone UI, an actual older binary and SSH transport were not exercised. Existing raw journal-byte admission, child-only Cancel and blank-required-question Skip limitations remain deferred and are disclosed on both replacements. The replacement descriptions and their one final status comment contain the evidence and intended/temporary differences. No source design changed during the final QA phase. This original PR was closed separately by |
|
Superseded by #29121 (keep pending chat decisions available outside loaded history) and #29125 (catch up desktop chats from the newest page, stacked on #29121). Review found that a newest-page catch-up could hide a still-pending subagent approval, so the pending requests became their own prerequisite PR. Closing with Brennan's OK. |
ELI5
Reopening a desktop chat that kept talking in the background can block Orca while it rereads every saved change. In a same-machine test with 20,000 missed revisions of one reply, reopening held the main thread for 277 ms at the median and 343 ms at the 95th percentile, parsing 20,099 stored rows before reaching the present.
Now a chat more than one history page behind opens on its newest page. Earlier messages remain available by scrolling up. A gap that fits one page still replays exactly as before.
What Changed
This draft is stacked on #28993, which settles acknowledged local message echoes even when their rows are outside a snapshot. Updated desktop clients use the existing latest-page snapshot fallback already used by phones when the first catch-up page reports more history ahead. The newest page uses the existing 200-row target and byte budget, keeping related subagent messages together. Older paired desktop clients keep exact replay unless they advertise that they can settle acknowledged echoes by journal position; newer clients talking to older hosts also keep the host's existing replay behavior. No new request field or stream event is introduced. The snapshot replaces the transcript and carries the current queue, background tasks, off-page subagent roster, latest turn, and live cursor through the existing publication path.
The renderer's existing snapshot handler invalidates older-page requests in flight and replaces the transcript and its paging boundary. The settlement prerequisite makes acknowledged local echoes safe when their rows fall outside that window. A current desktop client with an older host keeps the host's existing catch-up behavior; an older paired desktop client with this host keeps exact replay.
Why
Replay makes work grow with how long the chat was hidden, including thousands of obsolete copies of a streamed reply. A bounded current page removes that growth and uses the common pattern. Yielding between all missed pages would still read and parse the same obsolete data, while a new recovery protocol would duplicate the snapshot and older-history path already present.
Before / after measurements
Measured on openclaw / neil-ubuntu, Linux, Intel Core i3-12100F, Node 26.1.0. Base:
6837a4c1802; branch:7ceeeebf08b. Scratch benchmark uses the productionAgentSessionSubscribers.openpath backed by the real SQLite journal, serializes each emitted event, then JSON-decodes it and applies the production shared reducer. Times are p50 / p95 milliseconds across 31 measured reopens after 5 warmups, on the same machine and data. Main-thread time includes publication and JSON serialization; renderer time measures JSON decoding plus reducer application in Node, not browser paint or React rendering. Shared-machine load affects timings; operation counts are deterministic. The table measures the commits named above; the later current-work publication merge and send-settlement/capability follow-ups were not rebenchmarked.Each chat starts with 300 older messages and a loaded 200-message window. Two hidden-stream shapes: repeated revisions of one reply growing to 32 KiB (3.33 / 33.19 / 331.85 MB stored), and distinct 4 KiB messages (0.86 / 8.59 / 85.91 MB stored). The benchmark checks the final reply against the journal and walks older history after replacement to compare the complete transcript with the journal. No real chat database was copied or modified. Benchmarks are scratch files and are not part of the PR.
Repeated revisions of one streamed reply
Distinct 4 KiB messages
At 200 missed rows, both versions execute the same path and emit identical bytes; differences in timing are shared-machine noise. The first page still reads up to 201 journal rows to discover whether a replay fits, before choosing the existing byte-bounded snapshot. Snapshot construction still scans the reduced conversation; this change bounds replay work, not every operation by total conversation size.
Linked Issue
Native-chat performance investigation requested by @BrennanKB5; no separate issue.
Visual Proof
Hidden-window Electron on M4Air mac, commit
7ceeeebf08b, driven through Playwright CDP using a synthetic agent with 250 completed messages and 300 revisions of its latest reply. These are the same chat before hiding, after reopening, and after scrolling back; the before/after timing comparison is in the tables above.Before hiding, the visible chat has its first completed messages:
After the hidden stream, reopen shows the completed newest reply. The host sends one 200-item snapshot with older history available and the current cursor:
Real wheel input loads earlier messages: 400 → 503 loaded items, oldest sequence 259 → 3, no older page left, unchanged live cursor. The view stays on earlier history with “Jump to latest” available:
No console or page errors in this passing flow. The app ran in the background with an isolated home; real agent configuration hashes were unchanged, and the app and driver were stopped after QA.
Testing
7fcc8645fad: full PR CI and Mobile Checks passed, including the full PR aggregate, static analysis/typecheck, both packages, 119,546 passing unit tests across five shards, relay integration, and 231 released-client compatibility tests. The final run includes all 45 desktop sender tests, 18 web handshake tests, seven history read-budget tests, and the reader reopen contract.Review
Self-reviewed the host publication path, renderer snapshot replacement, stale older-page cancellation, queued draft publication, subagent roster, background tasks and latest-turn fields. The host implementation is shared by desktop and SSH servers and introduces no platform, workspace-kind, or provider-specific behavior. Live SSH transport was not exercised separately; Windows UI was exercised below.
Agent skill upstream boundary
Notes
Large-gap recovery replaces the loaded window, so a scrolled-back row outside the newest page must be loaded again by scrolling up. The existing history byte budget may shorten a page. This change does not bound snapshot construction by total retained chat size or change journal retention.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)Rendered QA
Full PR aggregate passed at
50b4118ccd4, including static analysis/typecheck, all five unit shards, wire compatibility, and Linux/Windows packages; Mobile verify also passed.Windows high spec, stack commit
50b4118ccd4c8d2fab2a68e14e4d5729c41df915(includes #28993 atc94bf143db3): private install and e2e build, one hidden Electron app, isolated profile, folder workspace, Claude stream-json stand-in, scripted Playwright over CDP. All captures assertisVisible() === false; no desktop input was used.A send was acknowledged at position 788 while the hidden reader remained at 787. Reopening after 786 missed saved changes produced one newest-page snapshot of 200 items at position 1573; the sent row was outside that snapshot, yet the pending echo cleared. The complete final reply rendered, and loading older history showed the delivered user message. Existing viewport prefetch then loaded 200 additional items; a subsequent real mouse-wheel scroll loaded the remaining 124 (400 → 524), with position 1573 unchanged. A short gap replayed 26 saved changes (526 → 534 items) without replacing the page. Clean quit/relaunch on a fresh CDP port restored the saved tab and all 531 message records with the same SHA-256 text hash.
The original
7fcc8645fadrun exposed a missing capability on the local desktop IPC reader: the same normal-reply fixture replayed 786 changes and retained 524 items; the corrected50b4118ccd4fixture reopened with a 200-item snapshot. The desktop capability advertisement and one subscription-boundary regression were added to #28951. This is a deterministic rendered count comparison, not a new timing benchmark. Live paired/SSH transport and phone UI were not exercised in this Windows run.Final reply after snapshot:
Delivered message after older history loads (same corrected commit, normal-reply fixture):
Older history after actual CDP wheel scrolling:
Transcript after clean restart: