Skip to content

perf(desktop): transcript tail hydration — 120-row tail page + on-demand older-page backfill - #87533

Merged
teknium1 merged 1 commit into
mainfrom
perf/desktop-tail-hydration
Aug 16, 2026
Merged

perf(desktop): transcript tail hydration — 120-row tail page + on-demand older-page backfill#87533
teknium1 merged 1 commit into
mainfrom
perf/desktop-tail-hydration

Conversation

@teknium1

@teknium1 teknium1 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Desktop hydrated every session transcript with a fixed limit: 500 REST read (getLatestSessionMessages), shipping and converting up to 500 rows on every session open, background-sync refresh, post-turn rehydrate, and tile resume — regardless of what the transcript window would ever paint.

This PR replaces that with a small tail page plus on-demand older-page backfill:

  • getLatestSessionMessages now requests limit: 120, order: 'latest', include_compacted: true (the includeCompacted flag is load-bearing for compaction-archived rows and is kept on every read). A new exported constant LATEST_SESSION_MESSAGES_LIMIT pins the page size.
  • New getOlderSessionMessages(id, profile, offset, limit=120) pages backwards from the newest message. Verified against gateway/platforms/api_server.py::_handle_session_messagesSessionDB.get_messages(latest=True): with order: 'latest', offset is measured back from the newest row and each page returns in chronological order — so after N tail rows, offset=N is exactly the next older page.
  • New store/transcript-tail.ts records per stored-session id whether the hydration page came back full (returned === limit ⇒ older rows likely exist) and the next offset, plus the owning profile so backfill routes to the same backend.
  • Show earlier backfill (app/chat/transcript-backfill.ts + ChatRuntimeBoundary in app/chat/index.tsx): the action still spends the DOM budget first, then the in-memory store window; once the whole in-memory transcript is materialized (windowed false) and the tail bookkeeping says truncated, it fetches the next older page and PREPENDS it to the session store via the session-state write path (sessionTileDelegate().updateSession). Prepends dedupe by durable row id (fallback: rendered message id), preserve reference identity on no-ops, share one in-flight fetch per stored session, and discard stale responses after a session switch (same shape as the isCurrentResume() guards in use-session-actions).
  • Legacy fallback: a backend that ignores pagination (no pagination metadata) returns the full transcript one-shot; the merge prepends the missing prefix and the bookkeeping marks the session fully loaded, retiring the REST action.
  • Refresh paths keep backfilled history: background sync (reconcileActiveTranscript), the post-turn rehydrate (wiring.tsx), warm re-activate, and the cold-resume prefetch now graft the refreshed newest-tail page onto any already-backfilled older prefix (graftRefreshedTailOntoBackfill) instead of clobbering it. When no anchor is found (compaction rewrite / different session) the refreshed tail stays authoritative — identical to pre-change behavior. The empty-REST-page reconciliation guard in use-session-actions (~L889) is untouched and still short-circuits before any graft.
  • use-background-sync and use-session-tile-delegate only ever needed the tail; they simply get the smaller page.

Request shape, before → after

Before After
Session open / refresh 1 × 500 messages 1 × 120 messages
Older history never loaded past 500 on-demand 120-row pages via "Show earlier"
Legacy backend (no pagination) full transcript full transcript (one-shot fallback, unchanged)

Validation

  • npx vitest run on the six touched suites (hermes.test.ts, transcript-backfill.test.ts, transcript-window.test.ts, use-session-actions.test.tsx, use-background-sync.test.ts, use-session-tile-delegate.test.ts): 7 files, 131 tests, all passing. New tests cover: (a) initial hydration requests limit 120 with order=latest&include_compacted=true; (b) backfill prepends the older page, dedupes by row id, preserves chronological order and reference identity; (c) a legacy response without pagination metadata falls back to the one-shot full transcript and retires the action; (d) a stale backfill response after a session switch is discarded with bookkeeping untouched.
  • npm run check:lint in apps/desktop (tsc on all three tsconfigs + eslint): 0 errors (pre-existing warnings only).

Related

Infographic

Transcript tail hydration

… older-page backfill

Replace the fixed 500-message REST hydration (getLatestSessionMessages)
with a 120-row newest-first tail page. When the page comes back full, a
new per-session tail store records "possibly truncated + next offset";
"Show earlier" — once the DOM budget and the in-memory store window are
both exhausted — fetches the next older page via the new
getOlderSessionMessages helper (order latest + offset, matching the
backend's back-from-newest paging semantics) and prepends it to the
session store, deduped by durable row id and race-guarded against
session switches. Legacy backends without pagination metadata fall back
to the one-shot full transcript and retire the action.

Tail-page refreshes (background sync, post-turn rehydrate, re-activate,
cold-resume prefetch) graft the refreshed tail onto any backfilled
prefix instead of clobbering it, preserving reference identity on
no-ops. includeCompacted stays on every read — compaction-archived rows
remain part of the durable display history.
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 9c33c20 — perf(desktop): hydrate transcripts with a small tail page +

⚠️ Warnings

CI timings · View report · View job

Wall time 39m24s vs 28m15s (+39.5%). 8 job(s) slower, 7 faster, 3 unchanged.

  • JS & TS checks / ui-tui / check: -24.0s
  • JS & TS checks / apps/desktop / check:test:ui:shard-3of3: +13.0s
  • JS & TS checks / apps/desktop / check:lint: +12.0s
  • JS & TS checks / apps/desktop / check:test:desktop:platforms: -10.0s
  • JS & TS checks / List npm workspaces: -10.0s

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

@teknium1
teknium1 merged commit 577093d into main Aug 16, 2026
38 checks passed
@teknium1
teknium1 deleted the perf/desktop-tail-hydration branch August 16, 2026 09:24
@alt-glitch alt-glitch added type/perf Performance improvement or optimization comp/desktop Electron desktop app (apps/desktop/*) area/sessions Session lifecycle, resume, persistence, history P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants