Skip to content

Bundle per-thread reads into GET /threads/:id?include= and seed the caches (B5) - #1903

Closed
SawyerHood wants to merge 3 commits into
bb/mobile-perf/sidebar-bootstrap-patchfrom
bb/mobile-perf/thread-bootstrap-include
Closed

Bundle per-thread reads into GET /threads/:id?include= and seed the caches (B5)#1903
SawyerHood wants to merge 3 commits into
bb/mobile-perf/sidebar-bootstrap-patchfrom
bb/mobile-perf/thread-bootstrap-include

Conversation

@SawyerHood

@SawyerHood SawyerHood commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

What was wrong

Opening a thread issued GET /threads/:id?include=environment,host and then five more DB-only GETs gated on it: pending interactions, queued messages, prompt history, default execution options, and tabs. On a phone over the connect tunnel this second hop delayed composer readiness by a full round-trip (mobile perf audit B5).

The per-thread hooks also passed staleTime: undefined, which TanStack treats as 0, so any seeded cache row was stale on arrival and the hook refetched on mount anyway.

What changed

  • packages/server-contract: threadIncludeOptionSchema gains pendingInteractions, queuedMessages, promptHistory, defaultExecutionOptions, tabs; threadWithIncludesResponseSchema gains the matching optional fields (present exactly when requested). SDK types follow from the contract; plugin SDK declaration bundle regenerated.
  • apps/server: buildThreadResponse fills the new fields through shared readers in services/threads/thread-detail-reads.ts; the stand-alone routes (/interactions, /queued-messages, /default-execution-options, /tabs) now call the same readers, so bundled and stand-alone responses cannot diverge. Stand-alone routes stay for CLI/SDK.
  • apps/app: the thread bootstrap requests all includes; ingestThreadDetailBootstrap seeds the five per-thread caches; useThreadPendingInteractions, useThreadQueuedMessages, useThreadDefaultExecutionOptions, useThreadTabs default to a 5 s THREAD_DETAIL_SEEDED_STALE_TIME_MS so seeded rows are fresh when the hooks mount. Realtime invalidation still refetches inside the window.

No daemon protocol change (server<->browser only).

How you verified

  • New server test apps/server/test/public/public-thread-data.test.ts "bundles per-thread reads through include= and matches the stand-alone routes": seeds a pending interaction, a queued message and tabs on an in-memory DB, asserts each bundled field deep-equals the stand-alone route response and that unrequested fields are absent. Fails before (400 invalid include).
  • New client test apps/app/src/hooks/queries/thread-queries.test.tsx "seeds the bundled per-thread caches so their hooks mount without extra requests": bootstrap resolves with bundled fields; the five hooks report data with zero calls to their SDK endpoints and isFetching false. Verified it fails when the stale window is removed.
  • pnpm exec turbo run typecheck for @bb/app, @bb/server, @bb/server-contract, @bb/sdk, @bb/cli, @get-bb/plugin-sdk: pass. turbo run lint --filter=@bb/app: 0 errors. Server public thread data/tabs/interactions/defaults + prompt-history suites: 123 passed. App suites touching the bootstrap/hooks (ThreadDetailPromptArea, EmbeddedThreadChat, AppLayout, PluginThreadChat, useThreadFileTabs, cache-owners, thread-queries): pass. @bb/server-contract and @bb/sdk suites: pass.

Fixes: part of the mobile / iOS Safari performance program (verified sweep report in the bb thread; no single issue).

Stack context

Layer 24 of 28 in the bb/mobile-perf/* stack (bottom → top: quick wins first, big rocks last).

  • Prerequisite (layer below): bb/mobile-perf/sidebar-bootstrap-patch (Patch sidebar rows from change metadata; weak ETag; search keepPreviousData (B1, B27, E8) #1902).
  • Next layer: bb/mobile-perf/composer-draft-pushdown (Push the composer draft subscription below ThreadDetailPromptArea (D1) #1904).
  • Audit findings addressed: see title IDs. Review: approved-with-fixes; 1 review fix commit(s).
  • Deliberately not done here: childSubset include (optional in the notes): not bundled. B10 (separate package) replaces that network read with a sidebar-cache derivation, so bundling it woul
  • Reviewer notes / follow-ups: Resume path (invalidateActiveThreadBundleQueriesAfterBrowserResume in apps/app/src/hooks/cache-owners/active-thread-lifecycle-cache-owner.ts) still invalidates the bootstrap AND th | A resume-time bootstrap refetch seeds the five caches unconditionally; an optimistic write (e.g. resolving an interaction, editing queued messages) that lands while that refetch is | useIsThreadDetailBootstrapPending (new gate for useThreadTabs) relies on the tabs hook only ever mounting under an active useThreadDetailBootstrap for the same thread; that holds t
  • Wire/contract: Server<->browser/SDK contract only: threadIncludeOptionSchema gains 5 values; threadWithIncludesResponseSchema gains 5 optional fields (present iff requested). packages/plugin-sdk/bundled-types/bb-plugin-sdk.d.ts regenerated (tracked, deterministic per Make plugin SDK declaration bundles deterministic #1851). No host-daemon wire change; HOST_DAEMON_PROTOCOL_VERSION not bumped (confirmed: no daemon payloads, RPC commands, or session messages touch

AGENT GENERATED: by Claude Opus 5

@SawyerHood SawyerHood changed the title bb/mobile perf/thread bootstrap include Bundle per-thread reads into GET /threads/:id?include= and seed the caches (B5) Aug 19, 2026
@SawyerHood
SawyerHood force-pushed the bb/mobile-perf/sidebar-bootstrap-patch branch from b8b9615 to ec7b0fd Compare August 19, 2026 07:42
@SawyerHood
SawyerHood force-pushed the bb/mobile-perf/thread-bootstrap-include branch from 61e0f29 to 93e1c49 Compare August 19, 2026 07:42
SawyerHood and others added 3 commits August 19, 2026 07:50
Opening a thread issued the bootstrap read (include=environment,host)
and then five more DB-only GETs gated on it: pending interactions,
queued messages, prompt history, default execution options, and tabs.
On a phone over the connect tunnel that second hop delayed the composer
becoming ready by a full round-trip.

Extend the include= option set with pendingInteractions, queuedMessages,
promptHistory, defaultExecutionOptions and tabs. Each bundled field is
present exactly when requested and is produced by the same reader as
its stand-alone route (moved into services/threads/thread-detail-reads
so the two surfaces cannot diverge). The stand-alone routes stay for
the CLI and SDK. The plugin SDK declaration bundle is regenerated for
the widened contract type. Server-to-browser only; no daemon protocol
change.

Co-Authored-By: Claude <noreply@anthropic.com>
The thread bootstrap now requests the bundled per-thread includes and
ingestThreadDetailBootstrap seeds the pending-interactions, queued-
messages, prompt-history, default-execution-options and tabs caches
from the one response.

Those hooks passed staleTime undefined, which TanStack treats as 0, so
seeded rows were stale on arrival and every hook still refetched its
own route on mount. Give them a shared 5 s seeded-freshness window
(THREAD_DETAIL_SEEDED_STALE_TIME_MS). Realtime invalidation still
refetches inside the window.

Co-Authored-By: Claude <noreply@anthropic.com>
…strap

The bundle seeds the tabs and pending-interaction caches, but both hooks
mount in the same commit as the bootstrap (useFixedPanelTabsState in the
thread-detail tree, the favicon probe in AppLayout), so useQuery decided
to fetch before the bundle could seed them and the /tabs and
/interactions requests still went out on every thread open. Wait for the
bootstrap to settle before enabling those two reads; a failed bootstrap
still lets them fetch on their own.

Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood
SawyerHood force-pushed the bb/mobile-perf/sidebar-bootstrap-patch branch from ec7b0fd to d2b37f3 Compare August 19, 2026 07:50
@SawyerHood
SawyerHood force-pushed the bb/mobile-perf/thread-bootstrap-include branch from 93e1c49 to c931531 Compare August 19, 2026 07:50
@SawyerHood
SawyerHood marked this pull request as ready for review August 19, 2026 08:04
@SawyerHood
SawyerHood marked this pull request as draft August 19, 2026 15:09
@SawyerHood

Copy link
Copy Markdown
Collaborator Author

Closing: the maintainer decided to land only layers 1–22 of the mobile-perf stack (#1880#1901). The branch stays on the remote for reference; it can be reopened and rebased later if the remaining big rocks (sidebar-bootstrap patching, thread bootstrap include=, composer draft push-down, timeline windowing, persisted cache, service worker) are picked up again.

@SawyerHood SawyerHood closed this Aug 19, 2026
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