feat(desktop,mobile,trpc): replace Electric sync with plain tRPC + React Query
Electric (the company) is winding down; this is PR 1 of 2 removing our dependency on it. This PR cuts Electric out of the app: every synced read becomes a plain tRPC + React Query query, dead code paths found during the audit are deleted, and nothing new is built — no push channel, no sync engine, no client data layer. PR 2 decommissions the infrastructure (apps/electric-proxy, docker-compose service, env/CSP plumbing, remaining deps) once the release ramp drains, because old desktop builds keep dialing the proxy until they age out — that's also why the proxy's shape allowlist still serves tables whose app-side features this PR deletes.
What this PR does
Deletions (dead code found during the audit)
- Dormant Durable Streams chat path:
apps/apichat REST/SSE routes (rode Electric Cloud's durable-streams service),DURABLE_STREAMS_*/TAVILY_*env + deploy plumbing,@durable-streams/client+@tavily/core, and the ChatPane cloud-session lifecycle. agent_commandscontrol plane: zero insert sites in the repo (the writer died with MCP v1) —useCommandWatcher, the collection, and the agent router are gone.v2_workspacespre-R1 fallback (hosts have been authoritative since R1),v2_clients(zero consumers), v1workspacescollection (zero consumers), mobile-onlyv2_projects.- The always-on network logger (Chromium netLog of the whole session partition with
includeSensitive, up to 4GB/user); a boot sweep reclaims stranded log dirs. - The Electric-only
jwt-refresh401 gate and desktop's@electric-sql/*deps.
Replacement (deliberately boring)
cloudTrpc:createTRPCReact<AppRouter>overhttpBatchStreamLinkwith bearer auth, sharing the renderer's existing QueryClient (IndexedDB persistence + Electron focus wiring already there).- Org-scoped read procedures porting
electric-proxy/where.tsscoping + column restrictions verbatim:organization.list/listMembers/listInvitations/listTeams,apiKey.list,chat.listSessions,v2Host.listMembers,project.list,automation.latestRuns,integration.github.listOrganizationPullRequests, plus shape widenings all verified within the proxy's column-restriction spec. - ~100 desktop
useLiveQuerysites rewritten touseQuery: settings screens fetch on mount + focus; always-visible surfaces (host presence, PR chips) poll at 30s; actively-watched screens (automation runs, tasks) at 5–15s; dropdown-gated queries useenabled: open. useOptimisticCollectionActionskeeps its exact API; internals now do React Query optimistic patches + tRPC mutations + invalidation.collections.tsretains only the 6 localStorage UI-state collections and their quota/heal hardening. Org switch drops org-scoped cloud queries from the QueryClient (replacing per-org collection eviction;analytics/deviceroots excluded — they exist on the electron router too).- Mobile (not actively used, non-gating): collections layer deleted; six consumed collections became plain
useQueryhooks.
Behavior changes made deliberately
automation.get/getPromptreads are org-scoped (Electric shape parity; the Team tab links to teammates' automations). Mutations stay owner-scoped.- The paywall gate defers on unresolved plan data (
ensureData) instead of resolving on an unknown: fail-open leaks gated actions to free users, fail-closed paywalls trialing orgs on every cold start. - Billing screens now treat
trialingas active (aligning withuseCurrentPlan/host-gating, which already did) and pick subscriptions deterministically (newest-first) instead of Electric arrival order.
Verification
- Full-repo typecheck passes;
bun run lintexits 0 (verified exit code). - Desktop test suite failure set is byte-identical to the base commit (30 pre-existing environment failures, zero regressions). CollectionsProvider/hooks/tasks suites pass.
- Grep sweep: zero remaining consumers of any Electric collection.
Follow-ups (deliberately not in this PR)
- Align
session.plan(packages/auth) withACTIVE_SUBSCRIPTION_STATUSES+ deterministic ordering — it currently disagrees withbilling.activePlanfor trialing orgs (touches web + mobile). TASKSgating is client-side only (no server-side plan check on the task router) — the deferred gate closes the cold-start window, but server enforcement is the real fix.- CurrentPlanCard renders "Renews " for trials where the date is trial end.
- Narrow
organization.listMembers' nested user shape (full row is Electric parity, incl. peers'organizationIds, but tighter is better). task.listis capped at 500 rows (Electric synced all); paginate if any org approaches that.- Cold-start: tasks/PR views show a brief load where Electric replayed from SQLite — add tRPC keys to the persister whitelist if it bothers anyone.
PR 2 checklist (after adoption drains)
Delete apps/electric-proxy + deploy job, docker-compose electric + Caddyfile, ELECTRIC_*/NEXT_PUBLIC_ELECTRIC_URL/EXPO_PUBLIC_ELECTRIC_URL plumbing (turbo/vite/env/CSP/build secrets/setup scripts), clearElectricCache, main-process SQLite persistence layer (now write-only), dead ELECTRIC_TASKS_ACCESS flag, packages/local-db synced-tables block; drop agent_commands/v2_clients tables (v2_workspaces after host-heartbeat retirement); update subprocessors.mdx (legal — remove both Electric and Tavily; Tavily's last consumer was the deleted web-search route, Slack's web search is Anthropic's server-side tool), changelog, AGENTS.md rule 9, and the 5 docs referencing OPTIMISTIC_ELECTRIC_UPDATES.md.
https://claude.ai/code/session_01Rq18Ske93pkaTqTHByebtq
Rebase note (2026-08-10)
Now stacked on #6331 (client version header) — merge that first; this PR's base is feat/client-version-header and should be retargeted to main after #6331 lands. Rebased across main's session-workspaces, kanban board, and workspace-tombstone work: their new Electric reads (automations stat cards, run→workspace linking, session/archived flows in useAccessibleV2Workspaces) were ported to the same tRPC procedures, with automation.latestRuns widened to carry v2WorkspaceId/chatSessionId/terminalSessionId. One deliberate narrowing: the automations page's 7-day created/failed stat cards now derive from the latest run per automation rather than all runs in the window (an org-scoped run-history procedure restores exact counts if wanted). Desktop full-suite deltas vs base are three notifyQuotaExhausted tests that pass in isolation — test-order mock leakage from the changed file set, not a code regression.
Runtime footgun audit (post-rebase, prompted by a live repro)
A dev-run surfaced Subscriptions are unsupported by httpBatchStreamLink — cloudTrpc lacked a dedicated React context, so its Provider shadowed electronTrpc's hooks (the PR #5877 trap). Fixed, plus an audit of every always-mounted cloud query:
chat.listSessionscapped at recent-200 for unfiltered reads; pane/run-link callers passsessionIdsand fetch exactly what they name.listOrganizationPullRequests(unbounded, org-wide) deleted — zero consumers after main moved PR chips to per-host queries.- Tasks are paginated: new
task.listPage(keyset, 100/page, Load-more on table/board, 200-row picker windows);task.listuntouched because released CLI/SDK/MCP clients depend on its array shape. Optimistic task patches cover both listPage cache shapes. - Verified acceptable: known-hosts 30s (rows≈hosts), failed-automations 30s (rows≈automations), dashboard sidebar does no cloud polling (local + host EventBus), intervals pause on window blur.
Summary by CodeRabbit
- New Features
- Added cloud-powered data loading across desktop and mobile for tasks, chats, hosts, organizations, members, billing, automations, and pull requests.
- Added paginated task loading with automatic loading for filtered and scrolling views.
- Added refreshed billing, API-key, team, invitation, and organization data views.
- Bug Fixes
- Improved stale-data handling when switching organizations.
- Workspace cleanup now completes locally without legacy cloud deletion.
- Removed
- Removed web workspace and terminal experiences, web search, and agent command tools.