Skip to content

fix(desktop): unblock v2 workspace render when Electric is slow#4141

Merged
saddlepaddle merged 1 commit intomainfrom
fix-workspace-loading-sta
May 6, 2026
Merged

fix(desktop): unblock v2 workspace render when Electric is slow#4141
saddlepaddle merged 1 commit intomainfrom
fix-workspace-loading-sta

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 6, 2026

Summary

  • After workspaces.create resolved end-to-end on the host service, the detail page (v2-workspace/layout.tsx) sat on WorkspaceCreatingState until Electric pushed the synced row into collections.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.
  • Cache the cloud row returned by the mutation on the in-flight entry; have the layout fall back to it while the live query is empty. Manager.tsx still cleans up the entry the moment Electric delivers, and the live query takes over seamlessly.
  • Cloud is the source of truth — no optimistic rollback machinery, no ghost-row failure mode (unlike the reverted feat(desktop): optimistic v2 workspace.create #4120).

Scope

  • Host-service workspaces.create now returns the full SelectV2Workspace row instead of a four-field projection. Threaded through registerCloudAndLocal, findExistingWorkspaceByBranch, and the final return.
  • adopt-existing-worktree.ts already returned the full row — no changes there.
  • SDK WorkspaceCreateResult.workspace widened to mirror the new shape; additive for all existing callers (CLI / MCP / automation dispatch only read id/name/branch).
  • Renderer InFlightEntry gains optional cloudRow; populated immediately after the mutation resolves; layout reads workspace ?? inFlight?.cloudRow.

Why this is safe

  • Existing UX gating in DashboardSidebarWorkspaceItem already disables the entire context menu (rename, host-change, move-to-section, etc.) for in-flight workspaces via isPending = !!creationStatus — so the only surface that mutates collections.v2Workspaces directly can't fire against a missing row.
  • All other consumers of collections.v2Workspaces (notifications controller, host pickers, useWorkspaceHostTarget) degrade gracefully — features briefly inert until Electric catches up.
  • WorkspaceProvider resolves hostUrl straight from workspace.hostId (not via useWorkspaceHostTarget), so the workspace tRPC connection wires up correctly from the cached row.

Test plan

  • Happy path: create a workspace from the modal — detail page flips from WorkspaceCreatingState to the real workspace immediately when the mutation resolves.
  • Slow Electric: block NEXT_PUBLIC_ELECTRIC_URL in DevTools after the mutation resolves — workspace renders normally via the cached cloud row; sidebar entry stays "creating" until Electric is unblocked, then merges seamlessly.
  • alreadyExists divergent id: confirm the early-remove branch in useWorkspaceCreates.ts still fires and redirect to canonical id works without flashing not-found.
  • Error path: kill host service mid-create — WorkspaceCreateErrorState renders as before; retry/dismiss work.
  • bun run lint && bun run typecheck pass 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.create and 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.

  • Bug Fixes
    • Renderer: cache the cloud row on the in‑flight entry and fall back to inFlight.cloudRow when collections.v2Workspaces hasn’t synced yet; clean up the entry once Electric delivers.
    • Host service: workspaces.create returns the full SelectV2Workspace row; SDK widens WorkspaceCreateResult.workspace (additive, no breaking changes).
    • Cloud remains the source of truth; no optimistic rollback paths.

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

Summary by CodeRabbit

  • New Features

    • Workspace creation API now returns expanded workspace metadata, including type, timestamps, organization and host information.
  • Refactor

    • Improved workspace resolution logic with fallback support for enhanced reliability during workspace initialization.

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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ef9f4863-ab55-4019-acdd-8aeba563bfd2

📥 Commits

Reviewing files that changed from the base of the PR and between 8037ade and c47b6e2.

📒 Files selected for processing (5)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/stores/workspace-creates/store.ts
  • apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
  • packages/host-service/src/trpc/router/workspaces/workspaces.ts
  • packages/sdk/src/resources/workspaces.ts

📝 Walkthrough

Walkthrough

The 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.

Changes

Workspace Immediate Display on Creation

Layer / File(s) Summary
Data Shape & Types
packages/sdk/src/resources/workspaces.ts, packages/host-service/src/trpc/router/workspaces/workspaces.ts
CloudWorkspace type introduced as the cloud-derived workspace representation. SDK's WorkspaceCreateResult expanded to include organizationId, hostId, type, createdByUserId, taskId, createdAt, and updatedAt fields.
Service Layer Refactoring
packages/host-service/src/trpc/router/workspaces/workspaces.ts
findExistingWorkspaceByBranch and registerCloudAndLocal now return CloudWorkspace instead of local-workspace shapes. Service treats cloud state as single source of truth for workspace data.
Store Infrastructure
apps/desktop/src/renderer/stores/workspace-creates/store.ts
InFlightEntry extended with optional cloudRow field and new markCloudRow method added to WorkspaceCreatesState to attach cloud row data to in-flight entries.
Creation Hook
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
After workspace creation, the returned cloud row is cached on the in-flight entry via markCloudRow, enabling immediate rendering before synced data arrives.
Layout Fallback
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
Workspace resolution now coalesces syncedWorkspace, in-flight cloudRow, and null; falling back to in-flight cloud row when synchronization hasn't completed.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A workspace blooms before the clouds align,
Its cloudRow cached, a treasure to divine.
The layout falls back with a hopeful glance,
While sync spins up—let data waltz and dance!
Swift as a hop, your form takes flight. ✨

✨ 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 fix-workspace-loading-sta

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 442c13e into main May 6, 2026
15 of 16 checks passed
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR fixes a UX freeze on the v2-workspace detail page when Electric sync is slow after a successful workspaces.create mutation. It caches the full cloud row returned by the host-service mutation on the in-flight store entry and falls back to it in the layout while Electric hasn't yet delivered the synced row.

  • Renderer: layout.tsx derives workspace as syncedWorkspace ?? inFlight?.cloudRow ?? null, so the page renders via WorkspaceProvider the moment the mutation resolves rather than waiting for Electric. Manager.tsx already removes the in-flight entry once Electric delivers, making the transition seamless.
  • Store: InFlightEntry gains an optional cloudRow?: SelectV2Workspace field with a corresponding markCloudRow action; useWorkspaceCreates.ts calls it immediately after the mutation succeeds.
  • Host service: workspaces.create now returns the full CloudWorkspace row (derived from v2Workspace.getFromHost) instead of a hand-rolled four-field projection, and WorkspaceCreateResult in the SDK is widened to match additively.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
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

@saddlepaddle saddlepaddle mentioned this pull request May 7, 2026
3 tasks
saddlepaddle added a commit that referenced this pull request May 7, 2026
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`.
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request May 8, 2026
…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.
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