Skip to content

fix(desktop): v2 file tree no longer waits on git.getStatus in shared tRPC batch#3400

Merged
saddlepaddle merged 1 commit into
mainfrom
its-taking-an-extremely-long-time-to-load-the-file-tree-for-workspaces-any-idea-why-can-you-look-int
Apr 13, 2026
Merged

fix(desktop): v2 file tree no longer waits on git.getStatus in shared tRPC batch#3400
saddlepaddle merged 1 commit into
mainfrom
its-taking-an-extremely-long-time-to-load-the-file-tree-for-workspaces-any-idea-why-can-you-look-int

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Apr 13, 2026

Summary

  • Root cause: the workspace tRPC client used httpBatchLink, which returns a batched response only after every procedure in the batch resolves. On v2 workspace switch, FilesTab's filesystem.listDirectory (a few ms of fs.readdir) was batched with WorkspaceSidebar's git.getStatus (5–10s on a large monorepo: runs git.status(), ls-files --others --ignored, buildBranch ×2, getChangedFilesForDiff, and git diff --numstat --cached in parallel). Result: the file tree stalled behind git for every workspace switch even though its own work was done in ms.
  • Fix: swap httpBatchLinkhttpBatchStreamLink in WorkspaceClientProvider. Same batching, but responses stream as each procedure completes, so fast calls return immediately and slow calls stream later on the same connection. No per-procedure routing needed.
  • Allow the trpc-accept header through the host-service CORS config (httpBatchStreamLink sends it to negotiate streamed responses; the existing allowHeaders list was rejecting it at preflight).
  • Drop a now-unnecessary staleTime: 0 override on listDirectory.fetch in useFileTree so React Query's default 5s stale window can serve rapid re-fetches from cache.

Explains all observations: v1 is unaffected (IPC, no HTTP batching), the Changes tab was fast because it re-used the already-cached git status, and the delay hit every switch because each new workspaceId warms a fresh git status.

Test plan

  • bun dev desktop, open a large monorepo workspace
  • Open DevTools Network panel filtered to /trpc
  • Switch between two v2 workspaces — file tree should render within the cost of one localhost round-trip while git.getStatus is still streaming
  • Click Changes tab — still populates correctly once git finishes
  • Switch back and forth a few times to confirm the fix holds on every switch
  • Confirm fs:events invalidation still refreshes the file tree after on-disk changes
  • bun run typecheck
  • bun run lint

Summary by cubic

Make the v2 file tree render immediately on workspace switch by streaming tRPC batch responses. Fixes long delays in large monorepos where git.getStatus was blocking filesystem.listDirectory.

  • Bug Fixes
    • Swapped httpBatchLinkhttpBatchStreamLink in WorkspaceClientProvider (@trpc/client) so each procedure streams back independently.
    • Allowed trpc-accept in host-service CORS to enable streamed responses.
    • Removed staleTime: 0 override on filesystem.listDirectory.fetch to use the default cache window.

Written for commit 4898520. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Chores
    • Updated CORS configuration to accept trpc-accept headers, improving request compatibility.
    • Refactored directory listing fetch behavior for consistency.
    • Updated data transport configuration to support streaming, enabling better performance for large data transfers.

… tRPC batch

The workspace tRPC client used httpBatchLink, which returns a batched response
only when every procedure in the batch resolves. On workspace switch, FilesTab's
filesystem.listDirectory (ms) was batched with WorkspaceSidebar's git.getStatus
(5-10s on a large monorepo), so the file tree stalled behind git for every
switch. Swap to httpBatchStreamLink so responses stream as each procedure
completes, allow the trpc-accept header through CORS on the host service, and
drop a now-unnecessary staleTime:0 override on listDirectory.fetch.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c8d66767-a6da-4c9c-be8b-e5f24e45c0bd

📥 Commits

Reviewing files that changed from the base of the PR and between 83c27a8 and 4898520.

📒 Files selected for processing (3)
  • apps/desktop/src/renderer/hooks/host-service/useFileTree/useFileTree.ts
  • packages/host-service/src/app.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx

📝 Walkthrough

Walkthrough

The PR modifies tRPC client-server configuration and request handling across three files: updating the workspace client's transport layer to use streaming, expanding CORS header allowances for trpc-accept, and refactoring a directory listing API call's parameter structure.

Changes

Cohort / File(s) Summary
tRPC Transport & Configuration
packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx, packages/host-service/src/app.ts
Updated tRPC transport from httpBatchLink to httpBatchStreamLink in the client; expanded CORS allowHeaders to include trpc-accept in server middleware.
API Call Signature Update
apps/desktop/src/renderer/hooks/host-service/useFileTree/useFileTree.ts
Refactored utils.filesystem.listDirectory.fetch invocation from positional arguments to single object payload; removed explicit staleTime: 0 option.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Streaming bundles hop with grace,
TRPC headers find their place,
API calls now stand refined,
Batch and stream perfectly aligned!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: using httpBatchStreamLink to prevent the file tree from blocking on slow git.getStatus calls during workspace switches.
Description check ✅ Passed The description provides a complete root cause analysis, clearly explains the fix across three files, includes testing steps, and follows the template structure with all critical sections filled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch its-taking-an-extremely-long-time-to-load-the-file-tree-for-workspaces-any-idea-why-can-you-look-int

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saddlepaddle saddlepaddle merged commit 5912c88 into main Apr 13, 2026
13 of 14 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Fly.io Electric (Fly.io) View App
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 13, 2026

Greptile Summary

This PR fixes a performance bottleneck in the v2 desktop workspace file tree by swapping httpBatchLink for httpBatchStreamLink in WorkspaceClientProvider. Previously, batched tRPC calls forced filesystem.listDirectory (fast, ~ms) to wait behind git.getStatus (slow, 5–10 s on large monorepos) before any response was delivered. With httpBatchStreamLink, each procedure in the batch streams its response as soon as it completes, so the file tree renders immediately while git status arrives later on the same connection. The companion CORS fix ("trpc-accept" header) is required for the streaming preflight to succeed.

Key changes:

  • WorkspaceClientProvider.tsxhttpBatchLinkhttpBatchStreamLink; root-cause fix, correct and minimal.
  • packages/host-service/src/app.ts — adds "trpc-accept" to CORS allowHeaders; required for httpBatchStreamLink preflight negotiation.
  • useFileTree.ts — removes staleTime: 0 from listDirectory.fetch; introduces a regression: fs:events-triggered forced refreshes that fire within 5 s of the last fetch will be served from cache, meaning on-disk changes (file create/delete) may go undetected in the file tree for up to 5 s. The fix is straightforward — forward { staleTime: 0 } only when force is true.

Confidence Score: 4/5

Safe to merge after addressing the staleTime regression in useFileTree; the core streaming fix and CORS change are correct.

Two of three changes are clean and correct. The one concrete bug — fs:events refreshes silenced by the 5-second React Query cache — requires a targeted one-line fix (force ? { staleTime: 0 } : undefined) before the PR fully delivers on its test plan item "Confirm fs:events invalidation still refreshes the file tree after on-disk changes."

apps/desktop/src/renderer/hooks/host-service/useFileTree/useFileTree.ts — staleTime removal breaks forced refreshes triggered by filesystem events within the 5-second cache window.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/hooks/host-service/useFileTree/useFileTree.ts Removes staleTime: 0 from listDirectory.fetch; the 5-second React Query cache now silences fs:events-triggered forced refreshes that happen within 5 s of the previous fetch, causing the file tree to miss on-disk changes in that window.
packages/host-service/src/app.ts Adds "trpc-accept" to CORS allowHeaders — required for httpBatchStreamLink preflight negotiation. Change is correct and minimal.
packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx Swaps httpBatchLinkhttpBatchStreamLink; correct root-cause fix that lets fast tRPC procedures (listDirectory) resolve and stream back before slow ones (git.getStatus) complete.

Sequence Diagram

sequenceDiagram
    participant FilesTab
    participant WorkspaceSidebar
    participant BatchStreamLink as httpBatchStreamLink
    participant HostService

    Note over FilesTab,HostService: Before (httpBatchLink) — file tree stalls behind git
    FilesTab->>BatchStreamLink: listDirectory (fast ~ms)
    WorkspaceSidebar->>BatchStreamLink: git.getStatus (slow 5-10s)
    BatchStreamLink->>HostService: Batched request
    Note over HostService: Both run in parallel on server
    HostService-->>BatchStreamLink: Response only after ALL complete (~10s)
    BatchStreamLink-->>FilesTab: listDirectory result (delayed ~10s)
    BatchStreamLink-->>WorkspaceSidebar: git.getStatus result

    Note over FilesTab,HostService: After (httpBatchStreamLink) — file tree renders immediately
    FilesTab->>BatchStreamLink: listDirectory (fast ~ms)
    WorkspaceSidebar->>BatchStreamLink: git.getStatus (slow 5-10s)
    BatchStreamLink->>HostService: Batched request (trpc-accept header negotiates stream)
    Note over HostService: Both run in parallel on server
    HostService-->>BatchStreamLink: listDirectory streams back (~ms)
    BatchStreamLink-->>FilesTab: listDirectory result (immediate)
    HostService-->>BatchStreamLink: git.getStatus streams back (~10s)
    BatchStreamLink-->>WorkspaceSidebar: git.getStatus result
Loading

Reviews (1): Last reviewed commit: "fix(desktop): v2 file tree no longer wai..." | Re-trigger Greptile

Comment on lines +218 to +221
const result = await utils.filesystem.listDirectory.fetch({
workspaceId,
absolutePath,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 fs:events refreshes silenced by 5-second stale window

Removing staleTime: 0 here introduces a real regression for the filesystem-event path. React Query's fetchQuery (what .fetch() delegates to) will return the in-cache response without going to the network whenever the data was fetched within the last STALE_TIME_MS (5 s). This conflicts with the fs:events handler, which calls loadDirectory(parentPath, true) as an explicit signal that on-disk state has changed and needs a fresh read.

Typical scenario:

  1. User switches workspace → listDirectory resolves in a few ms, result cached.
  2. User creates/deletes a file in the same terminal window (well within 5 s).
  3. fs:events fires → loadDirectory(parentPath, true) called.
  4. .fetch() returns the 5-seconds-stale cache entry — the new/deleted file is invisible.

The force flag already bypasses the internal loadedDirectories guard; it should also bypass the React Query cache. The minimal fix is to forward staleTime: 0 when force is true:

Suggested change
const result = await utils.filesystem.listDirectory.fetch({
workspaceId,
absolutePath,
});
const result = await utils.filesystem.listDirectory.fetch(
{ workspaceId, absolutePath },
force ? { staleTime: 0 } : undefined,
);

This keeps the 5-second cache for ordinary expand/collapse (fast path) while ensuring any forced refresh — whether from an fs:events event or an explicit refreshPath call — always hits the network.

MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 13, 2026
… tRPC batch (superset-sh#3400)

The workspace tRPC client used httpBatchLink, which returns a batched response
only when every procedure in the batch resolves. On workspace switch, FilesTab's
filesystem.listDirectory (ms) was batched with WorkspaceSidebar's git.getStatus
(5-10s on a large monorepo), so the file tree stalled behind git for every
switch. Swap to httpBatchStreamLink so responses stream as each procedure
completes, allow the trpc-accept header through CORS on the host service, and
drop a now-unnecessary staleTime:0 override on listDirectory.fetch.
@Kitenite Kitenite deleted the its-taking-an-extremely-long-time-to-load-the-file-tree-for-workspaces-any-idea-why-can-you-look-int branch April 13, 2026 16:35
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