fix(desktop): unblock v2 workspace render when Electric is slow#4141
fix(desktop): unblock v2 workspace render when Electric is slow#4141saddlepaddle merged 1 commit intomainfrom
Conversation
After workspaces.create resolves end-to-end on the host service, the detail page would sit on WorkspaceCreatingState until Electric pushed the synced row into collections.v2Workspaces. If Electric was slow or disconnected, the page perma-loaded even though the workspace was fully usable on the cloud and host. Cache the cloud row returned by the mutation on the in-flight entry and have the layout fall back to it while the live query is empty. Manager.tsx still cleans up the entry once Electric delivers, at which point the live query takes over seamlessly. No optimistic rollback machinery — cloud is the source of truth. Threads the full SelectV2Workspace through workspaces.create (host-service) and the SDK's WorkspaceCreateResult.workspace.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe PR enables the desktop app to immediately display newly created workspaces before server synchronization completes. It introduces a CloudWorkspace-centric model in the host service, extends the workspace-creates store to track cloud row data, caches returned cloud rows in the creation hook, and updates the layout to use the cached cloud row as a fallback for rendering. ChangesWorkspace Immediate Display on Creation
Sequence DiagramsequenceDiagram
participant User
participant Desktop App
participant Store
participant Creation Hook
participant Host Service
participant Cloud API
User->>Desktop App: Create workspace
Desktop App->>Store: Initialize in-flight entry
Store->>Creation Hook: Trigger workspace creation
Creation Hook->>Host Service: Call registerCloudAndLocal
Host Service->>Cloud API: Create and fetch workspace
Cloud API-->>Host Service: Return CloudWorkspace
Host Service-->>Creation Hook: Return CloudWorkspace
Creation Hook->>Store: markCloudRow(workspaceId, cloudRow)
Store->>Store: Attach cloudRow to in-flight entry
Desktop App->>Store: Query workspace for display
Store-->>Desktop App: Return syncedWorkspace || inFlight.cloudRow
Desktop App->>User: Display workspace immediately (from cached cloudRow)
Note over Cloud API,Store: Later: server sync completes
Cloud API-->>Store: Synced workspace arrives
Store->>Desktop App: Update with synced data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR fixes a UX freeze on the
Confidence Score: 5/5Safe to merge — the change is well-scoped, additive on the SDK surface, and cannot regress the error or not-found states. The fallback chain is sound: cloudRow is set only on a successful mutation, never on error or retry, so ghost rows are impossible. Manager.tsx cleanup is unchanged and fires reliably when Electric delivers. The alreadyExists divergent-id branch correctly gets a no-op markCloudRow followed by an explicit remove. The host-service change removes a hand-maintained field projection in favour of a derived type, reducing future drift risk. No files require special attention.
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx | Adds cloudRow fallback: workspace = syncedWorkspace ?? inFlight?.cloudRow ?? null. The early-return guard on !isReady prevents the fallback from triggering before the live query initialises, so the render order is deterministic and correct. |
| apps/desktop/src/renderer/stores/workspace-creates/store.ts | Adds optional cloudRow?: SelectV2Workspace to InFlightEntry and a markCloudRow action; implementation is a straightforward immutable map over entries matching by snapshot.id. |
| apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts | Calls markCloudRow(result.workspace.id, result.workspace) immediately after the mutation resolves. In the alreadyExists divergent-id case the markCloudRow call is a no-op and the explicit remove(workspaceId) handles cleanup correctly. |
| packages/host-service/src/trpc/router/workspaces/workspaces.ts | Replaces the hand-rolled 4-field ResolvedWorkspace projection with CloudWorkspace throughout. Eliminates duplicated field lists and ensures the returned shape stays in sync with the cloud API automatically. |
| packages/sdk/src/resources/workspaces.ts | WorkspaceCreateResult.workspace widened with additional fields. Change is additive — existing SDK callers only reading id/name/branch are unaffected. |
Reviews (1): Last reviewed commit: "fix(desktop): unblock v2 workspace rende..." | Re-trigger Greptile
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
npm has alpha.6 as the most recent published; alpha.7 was bumped in 71bf008 but never `npm publish`-ed. Skip alpha.7 on the registry and ship the current repo state as alpha.8. Changes since alpha.6: - workspaces.create adopts the canonical host-service shape (#3893) - automations.list accepts --name filter (#3952) - automations.prompt split into automations.prompt.get / .set (#3959) - agents.list (presets demoted to UI-only configuration) (#4097) - agents.run / workspaces.create gain `superset-chat` agent + `kind` discriminator on launch results (terminal vs chat) (#4116) - type adjustments for v2 workspace render path (#4141) - redact x-api-key in debug-log header dumps (#3956, was alpha.7) After merge: `cd packages/sdk && bun run build && cd dist && npm publish --access public`.
…rset-sh#4141) After workspaces.create resolves end-to-end on the host service, the detail page would sit on WorkspaceCreatingState until Electric pushed the synced row into collections.v2Workspaces. If Electric was slow or disconnected, the page perma-loaded even though the workspace was fully usable on the cloud and host. Cache the cloud row returned by the mutation on the in-flight entry and have the layout fall back to it while the live query is empty. Manager.tsx still cleans up the entry once Electric delivers, at which point the live query takes over seamlessly. No optimistic rollback machinery — cloud is the source of truth. Threads the full SelectV2Workspace through workspaces.create (host-service) and the SDK's WorkspaceCreateResult.workspace.
Summary
workspaces.createresolved end-to-end on the host service, the detail page (v2-workspace/layout.tsx) sat onWorkspaceCreatingStateuntil Electric pushed the synced row intocollections.v2Workspaces. If Electric was slow or the persisted shape was stale, the page perma-loaded even though the workspace was fully usable on the cloud and host.Manager.tsxstill cleans up the entry the moment Electric delivers, and the live query takes over seamlessly.Scope
workspaces.createnow returns the fullSelectV2Workspacerow instead of a four-field projection. Threaded throughregisterCloudAndLocal,findExistingWorkspaceByBranch, and the final return.adopt-existing-worktree.tsalready returned the full row — no changes there.WorkspaceCreateResult.workspacewidened to mirror the new shape; additive for all existing callers (CLI / MCP / automation dispatch only readid/name/branch).InFlightEntrygains optionalcloudRow; populated immediately after the mutation resolves; layout readsworkspace ?? inFlight?.cloudRow.Why this is safe
DashboardSidebarWorkspaceItemalready disables the entire context menu (rename, host-change, move-to-section, etc.) for in-flight workspaces viaisPending = !!creationStatus— so the only surface that mutatescollections.v2Workspacesdirectly can't fire against a missing row.collections.v2Workspaces(notifications controller, host pickers,useWorkspaceHostTarget) degrade gracefully — features briefly inert until Electric catches up.WorkspaceProviderresolveshostUrlstraight fromworkspace.hostId(not viauseWorkspaceHostTarget), so the workspace tRPC connection wires up correctly from the cached row.Test plan
WorkspaceCreatingStateto the real workspace immediately when the mutation resolves.NEXT_PUBLIC_ELECTRIC_URLin DevTools after the mutation resolves — workspace renders normally via the cached cloud row; sidebar entry stays "creating" until Electric is unblocked, then merges seamlessly.alreadyExistsdivergent id: confirm the early-remove branch inuseWorkspaceCreates.tsstill fires and redirect to canonical id works without flashing not-found.WorkspaceCreateErrorStaterenders as before; retry/dismiss work.bun run lint && bun run typecheckpass clean.Summary by cubic
Fixes the v2 workspace detail page hanging on “Creating” when Electric sync is slow by caching the cloud workspace row from
workspaces.createand rendering from it until the synced row arrives. The page now shows the workspace immediately after the mutation resolves, then hands off to live data when available.inFlight.cloudRowwhencollections.v2Workspaceshasn’t synced yet; clean up the entry once Electric delivers.workspaces.createreturns the fullSelectV2Workspacerow; SDK widensWorkspaceCreateResult.workspace(additive, no breaking changes).Written for commit c47b6e2. Summary will update on new commits.
Summary by CodeRabbit
New Features
Refactor