Skip to content

revert(desktop): optimistic v2 workspace.create (#4120)#4135

Merged
saddlepaddle merged 1 commit into
mainfrom
asdf-4
May 6, 2026
Merged

revert(desktop): optimistic v2 workspace.create (#4120)#4135
saddlepaddle merged 1 commit into
mainfrom
asdf-4

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 6, 2026

Summary

  • Reverts feat(desktop): optimistic v2 workspace.create #4120. The v2-workspace/layout.tsx if (workspace && !isSynced) branch catches any in-flight optimistic mutation, not just creates — so renaming a workspace flips $synced=false, the layout enters the "Creating workspace…" path, and workspace.createdAt.getTime() crashes the renderer.
  • The $synced-as-create-loading-signal pattern is too brittle; reverting until we have a separate "is this an in-flight create?" signal.

Notes

  • No DB migration changes in feat(desktop): optimistic v2 workspace.create #4120 — clean revert.
  • API v2Workspace.create drops the additive txid field. Existing 1.8.5 clients gracefully fall back to shape-stream sync (slightly longer create spinner, no crash).
  • Host-service ships bundled in Electron, no version skew.

Test plan

  • Rename a v2 workspace from the sidebar — no crash, name updates.
  • Create a new v2 workspace — appears, settles, no stuck spinner.
  • Failed v2 workspace create still surfaces the error state.

Summary by cubic

Reverts the optimistic v2 workspace.create flow and adds an explicit in‑flight creates store to fix crashes and unreliable loading states. Creation now calls the host service from the renderer and settles via shape sync (no txid).

  • Bug Fixes

    • Fixed crash when non-create mutations flipped $synced=false in v2 workspace layout.
    • Creation UI now shows reliable "Creating…" and error states with retry/dismiss.
    • In‑flight entries auto-clear when the real workspace row syncs.
  • Refactors

    • Added useWorkspaceCreatesStore and WorkspaceCreatesManager; removed useWorkspaceCreateFailuresStore.
    • Moved create handling out of collections.v2Workspaces.onInsert into useWorkspaceCreates (direct host call, pane layout setup, local state insert).
    • Removed txid from host-service and @trpc create APIs.
    • Sidebar no longer depends on $synced; simplified local main workspace auto-include and in-flight row injection; localized tabOrder helpers.

Written for commit 6aefdc9. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Dashboard sidebar now displays in-progress workspace creations with improved status tracking and visibility.
  • Bug Fixes

    • Simplified workspace creation error handling with clearer retry and dismiss options.
    • Enhanced pending workspace management in the dashboard sidebar for better user experience.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR refactors workspace creation from a failure-tracking model to an in-flight-tracking model, introduces a new WorkspaceCreatesManager component to synchronize in-flight entries with actual workspaces, updates the useWorkspaceCreates hook to provide explicit control methods (submit, retry, dismiss), removes Postgres transaction ID (txid) tracking from workspace creation responses, and relocates tab-order calculation helpers into a local module.

Changes

Workspace Creation: In-Flight Tracking and Hook Refactor

Layer / File(s) Summary
Store Refactoring
apps/desktop/src/renderer/stores/workspace-creates/store.ts
New InFlightEntry and WorkspaceCreatesState interfaces introduced; useWorkspaceCreatesStore replaces useWorkspaceCreateFailuresStore. In-flight store tracks creation state ("creating" or "error") with snapshot, error message, and start time.
Hook Refactoring
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
useWorkspaceCreates now returns UseWorkspaceCreatesApi with entries, submit, retry, and dismiss methods. Internal logic uses host service client and server-driven workflow, appends launches to pane layout, and handles idempotent workspace scenarios. Replaces optimistic insert approach with explicit state transitions.
Store Index & Manager
apps/desktop/src/renderer/stores/workspace-creates/index.ts, apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx
Index updated to export InFlightEntry, useWorkspaceCreatesStore, and WorkspaceCreatesManager (removes FailedWorkspaceCreate, useWorkspaceCreateFailuresStore). New WorkspaceCreatesManager component watches live workspace data via useLiveQuery and removes in-flight entries when their IDs appear in the workspace set.
Dashboard Sidebar Data
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
Replaced failure-tracking integration with in-flight store. Added inFlightEntries and inFlightSidebarRows mapping. Removed synced field selection; added explicit tabOrder and sectionId fields. Injects in-flight workspaces into group structure with proper ordering and creation status.
Dashboard Components & Layout
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceCreateErrorState/WorkspaceCreateErrorState.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
Sidebar item dismiss calls remove(id) on in-flight store. Layout mounts <WorkspaceCreatesManager />. Error state refactored to use useNavigate, call retry(workspaceId) and dismiss(workspaceId) from hook API, and navigate to /v2-workspaces on dismiss. Workspace layout derives create state from useWorkspaceCreatesStore entry keyed by workspaceId, removes $synced-based gating, conditionally renders creating or error states based on in-flight state field.
Collections & Tab Order
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/index.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/tabOrder.ts, apps/desktop/src/renderer/routes/_authenticated/hooks/useDashboardSidebarState/useDashboardSidebarState.ts
Removed onInsert handler from v2_workspaces collection (remote creation via host service); added imports for superjson, zod, heal utilities. Tab order functions (getPrependTabOrder, getNextTabOrder) removed from exported module and inlined in useDashboardSidebarState.
Host Service & TRPC Response Cleanup
packages/host-service/src/trpc/router/workspace-creation/shared/adopt-existing-worktree.ts, packages/host-service/src/trpc/router/workspaces/workspaces.ts, packages/trpc/src/router/v2-workspace/v2-workspace.ts
Removed txid field from AdoptExistingWorktreeResult and workspace creation responses. registerCloudAndLocal return type simplified; workspaceTxid variable removed. v2WorkspaceRouter.create now returns workspace row directly instead of wrapping with txid.

Sequence Diagram

sequenceDiagram
    participant User
    participant DashboardUI as Dashboard Sidebar UI
    participant useWorkspaceCreates as useWorkspaceCreates Hook
    participant Store as In-Flight Store<br/>(useWorkspaceCreatesStore)
    participant HostService as Host Service
    participant Collections as Collections & DB
    participant Manager as WorkspaceCreatesManager

    User->>DashboardUI: Trigger workspace create
    DashboardUI->>useWorkspaceCreates: submit(workspaceSpec)
    useWorkspaceCreates->>Store: add(entry with "creating" state)
    useWorkspaceCreates->>HostService: Create workspace via tRPC
    HostService->>Collections: Mutate/insert workspace row
    HostService-->>useWorkspaceCreates: Return workspace result
    useWorkspaceCreates->>Collections: Update local workspace state<br/>& pane layout
    
    par Parallel Monitoring
        Manager->>Collections: Query live workspace IDs
        Manager->>Store: Check in-flight entries
        alt Workspace ID matches in-flight
            Manager->>Store: remove(workspaceId)
        end
    end
    
    useWorkspaceCreates->>Store: mark "creating"/"error" state
    DashboardUI->>Store: Read in-flight entries
    DashboardUI->>User: Render pending workspace item
    
    User->>DashboardUI: Click retry or dismiss
    DashboardUI->>useWorkspaceCreates: retry(workspaceId) or dismiss(workspaceId)
    useWorkspaceCreates->>Store: Update/remove entry
    DashboardUI->>User: Reflect updated state
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • superset-sh/superset#4120: Directly modifies the workspace-creates store surface and related create/dismiss UI flows; switches from failure-tracking to in-flight tracking store and updates sidebar/error state integration.

Poem

🐰 A refactor hops with grace,
In-flight tracking finds its place,
No more failures, just a queue,
Creating states both clear and true!
Dismiss and retry, neat as can be,
Workspace journeys now flow free. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'revert(desktop): optimistic v2 workspace.create (#4120)' clearly and concisely describes the main change—reverting a previous optimization related to v2 workspace creation.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the summary (why the revert was needed), notes on safety/compatibility, and a test plan with specific scenarios.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 asdf-4

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR reverts the optimistic v2 workspace create pattern from #4120, replacing it with a direct host-service TRPC call inside useWorkspaceCreates and a new WorkspaceCreatesManager component that removes in-flight store entries once Electric delivers the real workspace row. The fix correctly addresses the crash: creating/error UI is now driven by the explicit in-flight store rather than $synced, so rename mutations (which also flip $synced=false) no longer enter the creating-spinner path.

  • Core behavioral change: onInsert handler removed from collections.v2Workspaces; workspace creation now calls the host-service directly from the hook, tracks progress in useWorkspaceCreatesStore, and lets WorkspaceCreatesManager clean up once Electric confirms the row.
  • txid dropped: The workspaces.create TRPC response no longer includes the Postgres transaction ID; existing clients gracefully fall back to shape-stream sync with a slightly longer spinner.
  • Sidebar ordering regression: Newly inserted v2WorkspaceLocalState entries use a hardcoded tabOrder: 0 rather than the getPrependTabOrder computation that previously ensured the new workspace lands at the top of its project lane.

Confidence Score: 4/5

The crash fix is correct and well-targeted; the revert cleanly removes $synced as a create-loading signal. The new flow is simpler and the guard in useDashboardSidebarData prevents double-display during the Electric-sync window.

Two observations hold back a clean merge: newly created workspace local state is always inserted with tabOrder: 0 instead of computing the prepend value, so workspace ordering within a project lane becomes non-deterministic when more than one workspace exists. Additionally, WorkspaceCreatesManager removes entries in any state (including error) as soon as their ID appears in the workspace collection, which could silently swallow an error banner in the rare case where the DB row lands before the TRPC error propagates back to the renderer.

apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts — the tabOrder: 0 insertion; apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx — unconditional removal of error-state entries.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts Core logic rewritten: optimistic Electric insert replaced with direct host-service TRPC call + in-flight store tracking. Local sidebar state insert uses hardcoded tabOrder: 0 instead of computing the correct prepend value.
apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx New component that watches v2Workspaces via live query and removes in-flight entries once the workspace appears in Electric — clean design, but removes entries regardless of their state (including error).
apps/desktop/src/renderer/stores/workspace-creates/store.ts Replaces useWorkspaceCreateFailuresStore (failures-only map) with useWorkspaceCreatesStore (array of in-flight entries with state creating
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx Fixes the crash: creating/error states now driven by the dedicated in-flight store instead of $synced, so rename mutations no longer trigger the creating-spinner path.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts Sidebar data now surfaces both creating and failed in-flight entries from the new store; pending worktrees no longer piggyback on $synced; localWorkspaceCandidates simplified to main-type-only query.
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts Removes the onInsert handler (and all its optimistic row / host-service call / txid machinery) from v2Workspaces. Logic moved to useWorkspaceCreates hook.
packages/host-service/src/trpc/router/workspaces/workspaces.ts Drops txid from the workspace create response — additive field no longer needed since optimistic inserts are gone.
packages/trpc/src/router/v2-workspace/v2-workspace.ts Removes getCurrentTxid calls and txid from the returned workspace row; simplifies create return paths.

Sequence Diagram

sequenceDiagram
    participant UI as Renderer UI
    participant Store as useWorkspaceCreatesStore
    participant Hook as useWorkspaceCreates
    participant Host as Host Service (TRPC)
    participant LocalState as v2WorkspaceLocalState
    participant Electric as Electric Sync (v2Workspaces)
    participant Manager as WorkspaceCreatesManager

    UI->>Hook: submit(args)
    Hook->>Store: add({ state: creating, ... })
    Hook->>Host: workspaces.create.mutate(snapshot)
    alt Success
        Host-->>Hook: { workspace, terminals, agents, alreadyExists }
        Hook->>LocalState: insert / update sidebarState + paneLayout
        Hook-->>UI: { ok: true, workspaceId }
        Note over UI,Store: UI shows WorkspaceCreatingState until Electric arrives
        Electric-->>Manager: workspace row arrives in v2Workspaces
        Manager->>Store: remove(id)
        UI->>UI: render workspace normally
    else alreadyExists at different id
        Host-->>Hook: { workspace, alreadyExists: true }
        Hook->>Store: remove(snapshotId)
        Hook-->>UI: { ok: true, workspaceId: canonicalId, alreadyExists: true }
    else Error
        Host-->>Hook: throws
        Hook->>Store: markError(workspaceId, message)
        Hook-->>UI: { ok: false, error }
        UI->>UI: render WorkspaceCreateErrorState
        UI->>Hook: retry(workspaceId)
        Hook->>Store: markCreating(workspaceId)
        Hook->>Host: workspaces.create.mutate(snapshot)
    end
Loading

Comments Outside Diff (2)

  1. apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts, line 84-100 (link)

    P2 New workspace always inserted with tabOrder: 0

    The old onInsert handler called getPrependTabOrder(projectTabOrders) so the new workspace landed at the top of its project lane. The new code uses the hardcoded value 0. When an existing workspace already holds tabOrder: 0 (or a negative value from a prior prepend), the new workspace will sort to the same position, and the sidebar order becomes non-deterministic for any project that already has workspaces.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
    Line: 84-100
    
    Comment:
    **New workspace always inserted with `tabOrder: 0`**
    
    The old `onInsert` handler called `getPrependTabOrder(projectTabOrders)` so the new workspace landed at the top of its project lane. The new code uses the hardcoded value `0`. When an existing workspace already holds `tabOrder: 0` (or a negative value from a prior prepend), the new workspace will sort to the same position, and the sidebar order becomes non-deterministic for any project that already has workspaces.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx, line 17-27 (link)

    P2 Error-state entries silently cleared when workspace appears in collections

    WorkspaceCreatesManager removes every in-flight entry whose ID is present in v2Workspaces, regardless of the entry's state. If a create call both inserts the DB row and throws an error (e.g., the TRPC call succeeds server-side but the response is corrupted), markError sets the entry to "error", but the Manager will then silently remove it once Electric delivers the row — the user never sees the error banner and has no indication of why the workspace appeared unexpectedly.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx
    Line: 17-27
    
    Comment:
    **Error-state entries silently cleared when workspace appears in collections**
    
    `WorkspaceCreatesManager` removes every in-flight entry whose ID is present in `v2Workspaces`, regardless of the entry's `state`. If a create call both inserts the DB row and throws an error (e.g., the TRPC call succeeds server-side but the response is corrupted), `markError` sets the entry to `"error"`, but the Manager will then silently remove it once Electric delivers the row — the user never sees the error banner and has no indication of why the workspace appeared unexpectedly.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts:84-100
**New workspace always inserted with `tabOrder: 0`**

The old `onInsert` handler called `getPrependTabOrder(projectTabOrders)` so the new workspace landed at the top of its project lane. The new code uses the hardcoded value `0`. When an existing workspace already holds `tabOrder: 0` (or a negative value from a prior prepend), the new workspace will sort to the same position, and the sidebar order becomes non-deterministic for any project that already has workspaces.

### Issue 2 of 2
apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx:17-27
**Error-state entries silently cleared when workspace appears in collections**

`WorkspaceCreatesManager` removes every in-flight entry whose ID is present in `v2Workspaces`, regardless of the entry's `state`. If a create call both inserts the DB row and throws an error (e.g., the TRPC call succeeds server-side but the response is corrupted), `markError` sets the entry to `"error"`, but the Manager will then silently remove it once Electric delivers the row — the user never sees the error banner and has no indication of why the workspace appeared unexpectedly.

Reviews (1): Last reviewed commit: "Revert "feat(desktop): optimistic v2 wor..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@saddlepaddle saddlepaddle merged commit f3ff72b into main May 6, 2026
16 of 17 checks passed
saddlepaddle added a commit that referenced this pull request May 6, 2026
The revert of #4120 (#4135) restored a hardcoded `tabOrder: 0` in the
v2 workspace.create path. Existing rows default to `tabOrder: 0` too,
so new workspaces tied with them and ordering was non-deterministic.

Restores the `getPrependTabOrder` helper from #4120 as a shared util in
`dashboardSidebarLocal/`, and uses it from `useWorkspaceCreates` so new
rows land strictly above every existing top-level item — matching what
the pending-row injection in `useDashboardSidebarData` already assumes.
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request May 8, 2026
)

The revert of superset-sh#4120 (superset-sh#4135) restored a hardcoded `tabOrder: 0` in the
v2 workspace.create path. Existing rows default to `tabOrder: 0` too,
so new workspaces tied with them and ordering was non-deterministic.

Restores the `getPrependTabOrder` helper from superset-sh#4120 as a shared util in
`dashboardSidebarLocal/`, and uses it from `useWorkspaceCreates` so new
rows land strictly above every existing top-level item — matching what
the pending-row injection in `useDashboardSidebarData` already assumes.
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