Skip to content

[codex] Revert optimistic workspace Electric transaction work#4744

Merged
saddlepaddle merged 1 commit into
mainfrom
revert-tanstack-electric
May 20, 2026
Merged

[codex] Revert optimistic workspace Electric transaction work#4744
saddlepaddle merged 1 commit into
mainfrom
revert-tanstack-electric

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 20, 2026

Summary

Reverts #4707 (feat: optimistic workspace creation + Electric write-sync correctness). This brings back the pre-#4707 workspace creation flow, including the renderer-side workspace-creates store/manager, and removes the TanStack DB optimistic workspace insert + Electric txid-confirmed sync rewrite.

Why

The TanStack DB / Electric SQL transaction work needs to be backed out while we revisit the approach. This PR is a straight revert of the merged squash commit 0a40cf2e.

Impact

Validation

  • bun run lint:fix
  • bun run lint

Open in Stage

Summary by cubic

Reverts the optimistic workspace creation and Electric write-sync path from #4707. Restores the previous renderer-driven create flow with in-flight tracking, removes txid-based sync, and simplifies server/router responses.

  • Refactors

    • Restore renderer-side in-flight tracking with useWorkspaceCreatesStore and WorkspaceCreatesManager; replace failedWorkspaceCreates local collection.
    • Sidebar and workspace UI now use creationStatus (preparing/generating/creating/failed); failed rows can be dismissed; shortcuts ignore in-flight items.
    • Cloud-row fallback: show created workspaces immediately from host response until Electric sync arrives.
    • Remove isSynced gating and deletion sync waits; simplify delete dialog.
    • Creation entry points (New Workspace, task/issue run, adopt worktrees) await submit() and toast success/failure; redirect to canonical id when it differs from the snapshot id.
    • Workspace route/title read from synced row or in-flight/cloud row; show creating/error states inline.
    • Drop txid plumbing across v2-workspace, v2-project, v2-host, and chat routers; mutations return rows/flags only. Simplify getCurrentTxid usage.
  • Dependencies

    • Remove @electric-sql/client from apps/electric-proxy; inline protocol params and drop Electric write-sync/error handling in collections.

Written for commit 74f82cb. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Added creation status tracking for in-flight workspaces with visual indicators (preparing, generating-branch, creating, failed).
    • Improved workspace creation error handling with retry and dismiss options.
  • Bug Fixes

    • Enhanced workspace state synchronization for faster UI updates.
    • Improved handling of failed workspace creation scenarios with better recovery flows.
  • Improvements

    • Better visual feedback during workspace creation process.
    • Optimized workspace availability detection and display in the interface.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 811ad2e8-96a9-4fa5-b919-1d83410e883d

📥 Commits

Reviewing files that changed from the base of the PR and between bc98ecb and 74f82cb.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • apps/desktop/src/renderer/hooks/host-service/useWorkspaceHostUrl/useWorkspaceHostUrl.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/hooks/useDestroyDialogState/useDestroyDialogState.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarCollapsedWorkspaceButton/DashboardSidebarCollapsedWorkspaceButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/utils/getCreationStatusText.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/utils/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/V2WorkspaceTitle/V2WorkspaceTitle.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspaceV2/OpenInWorkspaceV2.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunInWorkspacePopoverV2/RunInWorkspacePopoverV2.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunIssuesInWorkspacePopover/RunIssuesInWorkspacePopover.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
  • apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/hooks/useBranchPickerController/useBranchPickerController.ts
  • apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/hooks/useSubmitWorkspace/useSubmitWorkspace.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/workspaceSyncWaits.ts
  • apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx
  • apps/desktop/src/renderer/stores/workspace-creates/Manager.tsx
  • apps/desktop/src/renderer/stores/workspace-creates/index.ts
  • apps/desktop/src/renderer/stores/workspace-creates/store.ts
  • apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
  • apps/desktop/src/renderer/stores/workspace-creates/writeWorkspacePaneLayout.ts
  • apps/electric-proxy/package.json
  • apps/electric-proxy/src/electric.ts
  • packages/db/src/utils/sql.ts
  • packages/host-service/src/trpc/router/workspaces/workspaces.ts
  • packages/trpc/src/router/chat/chat.ts
  • packages/trpc/src/router/v2-host/v2-host.ts
  • packages/trpc/src/router/v2-project/v2-project.test.ts
  • packages/trpc/src/router/v2-project/v2-project.ts
  • packages/trpc/src/router/v2-workspace/v2-workspace.ts

📝 Walkthrough

Walkthrough

This PR refactors workspace creation tracking from Electric-based isSynced flags and failedWorkspaceCreates collections to a Zustand in-flight store. It updates sidebar and v2-workspace UI to render creationStatus instead of sync state, refactors the submission flow to return direct SubmitResult promises, and removes Electric transaction ID tracking.

Changes

In-flight workspace creation refactor with store, sidebar UI, and submission flow updates

Layer / File(s) Summary
In-flight store, types, and lifecycle manager
apps/desktop/src/renderer/stores/workspace-creates/store.ts, useWorkspaceCreates.ts, Manager.tsx, index.ts
New Zustand store tracks in-flight workspace creation state (creating/error), cached cloud rows, and timestamps; WorkspaceCreatesManager syncs store entries with live workspace query to auto-clean completed creations.
Sidebar workspace list and in-flight entry injection
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts, useDashboardSidebarShortcuts.ts, types.ts
Integrates in-flight entries into sidebar UI by generating pending sidebar rows (with PENDING_WORKSPACE_TAB_ORDER); adds cloud-row fallback workspaces for in-flight entries not yet synced; removes isSynced from workspace queries; injects pending entries as local-device workspace items into project children.
Creation status text utility
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/utils/getCreationStatusText.ts, index.ts
New helper maps creationStatus values (preparing, generating-branch, creating, failed) to human-readable UI labels.
Sidebar workspace components rendering creation status
DashboardSidebarWorkspaceItem.tsx, DashboardSidebarCollapsedWorkspaceButton.tsx, DashboardSidebarExpandedWorkspaceRow.tsx, DashboardSidebarWorkspaceIcon.tsx
Updates dashboard sidebar workspace components to derive state from creationStatus instead of isSynced; renders creation-status text, error exclamation icon on failed status, and conditional button labels; wires dismiss action for failed in-flight creations.
Workspace submission hook refactor
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts
Rewrites submission to call host service directly and update local collections immediately; exposes entries, retry, and dismiss actions alongside submit; changes submit to return Promise<SubmitResult> directly instead of a handle with completed promise; removes optimistic DB writes and pane layout writing via writeWorkspacePaneLayout.
Workspace submission consumers
useSubmitWorkspace.ts, useBranchPickerController.ts, OpenInWorkspaceV2.tsx, RunInWorkspacePopoverV2.tsx, RunIssuesInWorkspacePopover.tsx, adopt-worktrees/page.tsx
Updates consumers to await the new direct submit promise and check result.ok for success/failure; consolidates navigation and error handling around the single submitted promise; removes prior completed.then(...) bifurcation.
V2 workspace layout and error state
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx, WorkspaceCreateErrorState.tsx
Updates layout to derive workspace from synced rows or in-flight cloud row; renders creating/error states from in-flight entry instead of failed-creation query; refactors error state component to accept explicit props and call retry/dismiss from hook.
Dashboard layout integration
apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx
Mounts WorkspaceCreatesManager to keep the in-flight store synchronized with live workspace rows.
Electric collections and sync waits cleanup
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts, dashboardSidebarLocal/schema.ts, workspaceSyncWaits.ts
Removes waitForWorkspaceDeleted function; removes failedWorkspaceCreates from OrgCollections; removes Electric 401-refresh error handling and ELECTRIC_WRITE_SYNC_TIMEOUT_MS constant; removes FailedWorkspaceCreateRow schema/types.
Database and host service transaction ID cleanup
packages/db/src/utils/sql.ts, packages/host-service/src/trpc/router/workspaces/workspaces.ts, packages/trpc/src/router/v2-workspace/v2-workspace.ts, v2-project/v2-project.ts, chat/chat.ts, v2-host/v2-host.ts, v2-project/v2-project.test.ts
Simplifies getCurrentTxid SQL casting to ::text; removes txid from workspace/project/chat/host router responses; removes transaction wrapping where only txid was captured; updates test mocks for string-based txid.
V2 workspace title fallback
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/V2WorkspaceTitle/V2WorkspaceTitle.tsx
Updates component to derive name/branch by preferring synced workspace values and falling back to in-flight cloud-row or snapshot values.
Electric proxy cleanup
apps/electric-proxy/package.json, apps/electric-proxy/src/electric.ts
Removes @electric-sql/client dependency; hard-codes PROTOCOL_PARAMS set instead of importing from library.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • superset-sh/superset#4135: Both refactor workspace create/in-flight UI gating away from $synced and toward workspace-creates store state in v2-workspace/layout and create components.
  • superset-sh/superset#4338: Both update workspace submission result handling in RunIssuesInWorkspacePopover.tsx and the multi-issue workspace creation flow.
  • superset-sh/superset#4493: Both modify DashboardSidebarWorkspace type and sidebar data plumbing; main PR adds creationStatus, retrieved PR adds taskId.

Poem

🐰 A rabbit hops through workspace creation land,
No more sync-state delays, in-flight we stand!
Zustand store tracks each creation's pace,
From pending to ready, a much faster race!

✨ 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 revert-tanstack-electric

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 20, 2026

@saddlepaddle saddlepaddle marked this pull request as ready for review May 20, 2026 00:12
@saddlepaddle saddlepaddle merged commit 9d8a77c into main May 20, 2026
15 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 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

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This PR reverts the optimistic workspace creation and Electric write-sync path from #4707, restoring a renderer-side in-flight tracking store (useWorkspaceCreatesStore) and removing txid-based Electric confirmation across the v2-workspace, v2-project, v2-host, and chat routers.

  • Workspace creation flow: replaces TanStack DB optimistic inserts with a Zustand InFlightEntry store; the WorkspaceCreatesManager removes entries once Electric delivers the synced row.
  • Server mutations simplified: txid return values dropped from workspace create/delete, v2-host mutations, and chat session create; several NOT_FOUND guards removed from v2-host routes.
  • Electric proxy: drops @electric-sql/client dependency and inlines the protocol query-param list; removes per-collection onError JWT-refresh handlers.

Confidence Score: 4/5

Safe to merge as a revert; no new functional regressions were introduced beyond a few small gaps in the restoration.

The PR faithfully reverts the workspace Electric-sync work and the new in-flight store machinery is straightforward. The main concerns are: getCurrentTxid casts xid8 directly to text without the 32-bit truncation or safe-integer guard; the test mock returns a string where the real function returns a number; keyboard-shortcut filtering dropped the isDeleting guard; and PENDING_WORKSPACE_TAB_ORDER shares the same sentinel as MAIN_WORKSPACE_TAB_ORDER. None of these are blocking.

packages/db/src/utils/sql.ts (txid cast precision), packages/trpc/src/router/v2-project/v2-project.test.ts (mock type mismatch), and useDashboardSidebarShortcuts.ts (missing isDeleting filter).

Important Files Changed

Filename Overview
packages/db/src/utils/sql.ts getCurrentTxid now returns pg_current_xact_id()::text (64-bit xid8) instead of ::xid::text (32-bit); the isSafeInteger guard is also removed, leaving no protection against precision loss on very large txids.
packages/trpc/src/router/v2-project/v2-project.test.ts getCurrentTxidMock changed to return string "txid-123" while the real getCurrentTxid returns number; uploadIcon/resetIconToGitHub/removeIcon assertions now check txid: "txid-123" making the mock type-inconsistent with production.
apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts Replaces optimistic TanStack DB insert with direct host-service call; stores in-flight state in Zustand; submit/retry/dismiss API looks correct.
packages/trpc/src/router/v2-host/v2-host.ts renameHost and updateHostMemberRole drop NOT_FOUND guards; operations now silently succeed even when target row doesn't exist. Part of intentional revert but changes error-reporting contract.
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts Removes failedWorkspaceCreates collection, Electric onError JWT-refresh handlers, and workspace insert mutation; electricTxidMatch replaced with direct { txid } return objects.
apps/electric-proxy/src/electric.ts Protocol params inlined as a string set instead of imported from @electric-sql/client; will need manual update if Electric adds/renames protocol params in future releases.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts PENDING_WORKSPACE_TAB_ORDER and MAIN_WORKSPACE_TAB_ORDER both set to Number.MIN_SAFE_INTEGER; in-flight workspaces injected with cloud-row fallback path added cleanly.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts isDeleting() filter removed from workspace shortcut list; workspaces currently being deleted are no longer excluded from keyboard shortcuts.

Sequence Diagram

sequenceDiagram
    participant UI as Renderer UI
    participant Store as WorkspaceCreatesStore
    participant Manager as WorkspaceCreatesManager
    participant Host as Host Service
    participant Electric as Electric Sync

    UI->>Store: "add({ state: "creating" })"
    UI->>UI: navigate to /v2-workspace/:id (optimistic)
    UI->>Host: workspaces.create.mutate(snapshot)
    alt success
        Host-->>UI: "{ workspace, terminals, agents, alreadyExists }"
        UI->>Store: markCloudRow(workspace.id, workspace)
        UI->>Collections: insert/update v2WorkspaceLocalState
        Note over UI,Electric: workspace detail renders from cloudRow fallback
        Electric-->>Collections: v2Workspaces row delivered
        Manager->>Store: remove(workspace.id)
        Collections-->>UI: live query updates sidebar
    else error
        Host-->>UI: throws
        UI->>Store: markError(workspaceId, error)
        UI->>UI: show WorkspaceCreateErrorState
        UI->>Store: retry / dismiss
    end
Loading
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
packages/trpc/src/router/v2-project/v2-project.test.ts:4
The mock now returns a string `"txid-123"` but the real `getCurrentTxid` returns `Number.parseInt(txid, 10)` — a `number`. The `uploadIcon`, `resetIconToGitHub`, and `removeIcon` test assertions that check `txid: "txid-123"` are therefore validating against the wrong type. If the txid type ever regresses from number to string the tests would still pass, defeating the purpose of the assertion.

```suggestion
const getCurrentTxidMock = mock(async () => 123);
```

### Issue 2 of 4
packages/db/src/utils/sql.ts:24-33
`pg_current_xact_id()` returns an `xid8` (64-bit unsigned integer). Casting it directly to `text` and then calling `Number.parseInt` is safe today, but `xid8` values can exceed `Number.MAX_SAFE_INTEGER` (2⁵³−1) on a sufficiently busy/long-running database, at which point `parseInt` silently returns an imprecise value. The previous code used `::xid::text` to truncate to 32 bits before parsing and guarded with `Number.isSafeInteger`. Restoring either protection prevents a hard-to-diagnose mismatch between the value stored server-side and the value Electric waits for.

```suggestion
	const result = await tx.execute<{ txid: string }>(
		sql`SELECT pg_current_xact_id()::xid::text as txid`,
	);

	const raw = result.rows[0]?.txid;
	const txid = raw === undefined ? Number.NaN : Number.parseInt(raw, 10);
	if (!Number.isSafeInteger(txid)) {
		throw new Error(`Failed to get valid txid: ${raw}`);
	}

	return txid;
```

### Issue 3 of 4
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts:24-27
`PENDING_WORKSPACE_TAB_ORDER` and `MAIN_WORKSPACE_TAB_ORDER` are both `Number.MIN_SAFE_INTEGER`. When a project has both a main workspace and a pending in-flight workspace at the same time, they share the same sort key, which can cause non-deterministic ordering. A distinct sentinel (e.g., `Number.MIN_SAFE_INTEGER + 1`) makes the intent explicit.

```suggestion
// Sits above every real workspace so the pending row lines up with the real one,
// which is inserted via getPrependTabOrder.
const PENDING_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER + 1;
const MAIN_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
```

### Issue 4 of 4
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts:56-62
**Missing `isDeleting` guard on keyboard shortcuts**

The previous filter excluded both non-synced workspaces and workspaces actively being deleted (`isDeleting`). The new filter only excludes workspaces with a `creationStatus`, so a workspace whose delete API call has been sent but whose Electric deletion event hasn't arrived yet will still receive a `⌘N` shortcut and be navigatable. `DeletingWorkspacesProvider` still tracks this state; the `isDeleting` check and its import were dropped from this file as part of the revert but can be restored with two lines.

Reviews (1): Last reviewed commit: "Revert "feat: optimistic workspace creat..." | Re-trigger Greptile

import { TRPCError, type TRPCRouterRecord } from "@trpc/server";

const getCurrentTxidMock = mock(async () => 123);
const getCurrentTxidMock = mock(async () => "txid-123");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The mock now returns a string "txid-123" but the real getCurrentTxid returns Number.parseInt(txid, 10) — a number. The uploadIcon, resetIconToGitHub, and removeIcon test assertions that check txid: "txid-123" are therefore validating against the wrong type. If the txid type ever regresses from number to string the tests would still pass, defeating the purpose of the assertion.

Suggested change
const getCurrentTxidMock = mock(async () => "txid-123");
const getCurrentTxidMock = mock(async () => 123);
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/trpc/src/router/v2-project/v2-project.test.ts
Line: 4

Comment:
The mock now returns a string `"txid-123"` but the real `getCurrentTxid` returns `Number.parseInt(txid, 10)` — a `number`. The `uploadIcon`, `resetIconToGitHub`, and `removeIcon` test assertions that check `txid: "txid-123"` are therefore validating against the wrong type. If the txid type ever regresses from number to string the tests would still pass, defeating the purpose of the assertion.

```suggestion
const getCurrentTxidMock = mock(async () => 123);
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 24 to +33
const result = await tx.execute<{ txid: string }>(
sql`SELECT pg_current_xact_id()::xid::text as txid`,
sql`SELECT pg_current_xact_id()::text as txid`,
);

const raw = result.rows[0]?.txid;
const txid = raw === undefined ? Number.NaN : Number.parseInt(raw, 10);
if (!Number.isSafeInteger(txid)) {
throw new Error(`Failed to get valid Electric txid: ${raw}`);
const txid = result.rows[0]?.txid;
if (!txid) {
throw new Error("Failed to get current transaction ID");
}

return txid;
return Number.parseInt(txid, 10);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 pg_current_xact_id() returns an xid8 (64-bit unsigned integer). Casting it directly to text and then calling Number.parseInt is safe today, but xid8 values can exceed Number.MAX_SAFE_INTEGER (2⁵³−1) on a sufficiently busy/long-running database, at which point parseInt silently returns an imprecise value. The previous code used ::xid::text to truncate to 32 bits before parsing and guarded with Number.isSafeInteger. Restoring either protection prevents a hard-to-diagnose mismatch between the value stored server-side and the value Electric waits for.

Suggested change
const result = await tx.execute<{ txid: string }>(
sql`SELECT pg_current_xact_id()::xid::text as txid`,
sql`SELECT pg_current_xact_id()::text as txid`,
);
const raw = result.rows[0]?.txid;
const txid = raw === undefined ? Number.NaN : Number.parseInt(raw, 10);
if (!Number.isSafeInteger(txid)) {
throw new Error(`Failed to get valid Electric txid: ${raw}`);
const txid = result.rows[0]?.txid;
if (!txid) {
throw new Error("Failed to get current transaction ID");
}
return txid;
return Number.parseInt(txid, 10);
const result = await tx.execute<{ txid: string }>(
sql`SELECT pg_current_xact_id()::xid::text as txid`,
);
const raw = result.rows[0]?.txid;
const txid = raw === undefined ? Number.NaN : Number.parseInt(raw, 10);
if (!Number.isSafeInteger(txid)) {
throw new Error(`Failed to get valid txid: ${raw}`);
}
return txid;
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/db/src/utils/sql.ts
Line: 24-33

Comment:
`pg_current_xact_id()` returns an `xid8` (64-bit unsigned integer). Casting it directly to `text` and then calling `Number.parseInt` is safe today, but `xid8` values can exceed `Number.MAX_SAFE_INTEGER` (2⁵³−1) on a sufficiently busy/long-running database, at which point `parseInt` silently returns an imprecise value. The previous code used `::xid::text` to truncate to 32 bits before parsing and guarded with `Number.isSafeInteger`. Restoring either protection prevents a hard-to-diagnose mismatch between the value stored server-side and the value Electric waits for.

```suggestion
	const result = await tx.execute<{ txid: string }>(
		sql`SELECT pg_current_xact_id()::xid::text as txid`,
	);

	const raw = result.rows[0]?.txid;
	const txid = raw === undefined ? Number.NaN : Number.parseInt(raw, 10);
	if (!Number.isSafeInteger(txid)) {
		throw new Error(`Failed to get valid txid: ${raw}`);
	}

	return txid;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +24 to 27
// Sits above every real workspace so the pending row lines up with the real one,
// which is inserted via getPrependTabOrder.
const PENDING_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
const MAIN_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 PENDING_WORKSPACE_TAB_ORDER and MAIN_WORKSPACE_TAB_ORDER are both Number.MIN_SAFE_INTEGER. When a project has both a main workspace and a pending in-flight workspace at the same time, they share the same sort key, which can cause non-deterministic ordering. A distinct sentinel (e.g., Number.MIN_SAFE_INTEGER + 1) makes the intent explicit.

Suggested change
// Sits above every real workspace so the pending row lines up with the real one,
// which is inserted via getPrependTabOrder.
const PENDING_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
const MAIN_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
// Sits above every real workspace so the pending row lines up with the real one,
// which is inserted via getPrependTabOrder.
const PENDING_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER + 1;
const MAIN_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
Line: 24-27

Comment:
`PENDING_WORKSPACE_TAB_ORDER` and `MAIN_WORKSPACE_TAB_ORDER` are both `Number.MIN_SAFE_INTEGER`. When a project has both a main workspace and a pending in-flight workspace at the same time, they share the same sort key, which can cause non-deterministic ordering. A distinct sentinel (e.g., `Number.MIN_SAFE_INTEGER + 1`) makes the intent explicit.

```suggestion
// Sits above every real workspace so the pending row lines up with the real one,
// which is inserted via getPrependTabOrder.
const PENDING_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER + 1;
const MAIN_WORKSPACE_TAB_ORDER = Number.MIN_SAFE_INTEGER;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 56 to 62
const flattenedWorkspaces = useMemo(
() =>
groups
.flatMap((project) => getProjectChildrenWorkspaces(project.children))
.filter((workspace) => workspace.isSynced && !isDeleting(workspace.id)),
[groups, isDeleting],
.filter((workspace) => !workspace.creationStatus),
[groups],
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing isDeleting guard on keyboard shortcuts

The previous filter excluded both non-synced workspaces and workspaces actively being deleted (isDeleting). The new filter only excludes workspaces with a creationStatus, so a workspace whose delete API call has been sent but whose Electric deletion event hasn't arrived yet will still receive a ⌘N shortcut and be navigatable. DeletingWorkspacesProvider still tracks this state; the isDeleting check and its import were dropped from this file as part of the revert but can be restored with two lines.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
Line: 56-62

Comment:
**Missing `isDeleting` guard on keyboard shortcuts**

The previous filter excluded both non-synced workspaces and workspaces actively being deleted (`isDeleting`). The new filter only excludes workspaces with a `creationStatus`, so a workspace whose delete API call has been sent but whose Electric deletion event hasn't arrived yet will still receive a `⌘N` shortcut and be navigatable. `DeletingWorkspacesProvider` still tracks this state; the `isDeleting` check and its import were dropped from this file as part of the revert but can be restored with two lines.

How can I resolve this? If you propose a fix, please make it concise.

sazabi Bot pushed a commit that referenced this pull request May 20, 2026
sazabi Bot pushed a commit that referenced this pull request May 20, 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