|
1 | 1 | # Replace React Query With AtomRpc + Atom State |
2 | 2 |
|
3 | 3 | ## Summary |
4 | | -- Use `effect/unstable/reactivity/AtomRpc` over the existing `WsRpcGroup`; stop wrapping RPC in promises via [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts) and [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts). |
| 4 | +- Use `effect/unstable/reactivity/AtomRpc` over the existing `WsRpcGroup`; stop wrapping RPC in promises via [wsRpcClient.ts](apps/web/src/wsRpcClient.ts) and [wsNativeApi.ts](apps/web/src/wsNativeApi.ts). |
5 | 5 | - Keep Zustand for orchestration read model and UI state. |
6 | 6 | - Keep a narrow `desktopBridge` adapter for dialogs, menus, external links, theme, and updater APIs. |
7 | 7 | - Do not introduce Suspense in this migration. Atom-backed hooks should keep returning `data`, `error`, `isLoading|isPending`, `refresh`, and `mutateAsync`-style surfaces so component churn stays low. |
8 | 8 |
|
9 | 9 | ## Target Architecture |
10 | | -- Extract the websocket `RpcClient.Protocol` layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) into `rpc/protocol.ts`. |
| 10 | +- Extract the websocket `RpcClient.Protocol` layer from [wsTransport.ts](apps/web/src/wsTransport.ts) into `rpc/protocol.ts`. |
11 | 11 | - Define one `AtomRpc.Service` for `WsRpcGroup` in `rpc/client.ts`. |
12 | 12 | - Add `rpc/invalidation.ts` with explicit scoped invalidation keys: `git:${cwd}`, `project:${cwd}`, `checkpoint:${threadId}`, `server-config`. |
13 | 13 | - Add `platform/desktopBridge.ts` as the only browser/desktop facade. |
14 | | -- Remove from web by the end: [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts), [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts), [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts), [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx), [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts), and all `*ReactQuery.ts` modules. |
| 14 | +- Remove from web by the end: [wsNativeApi.ts](apps/web/src/wsNativeApi.ts), [nativeApi.ts](apps/web/src/nativeApi.ts), [wsNativeApiState.ts](apps/web/src/wsNativeApiState.ts), [wsNativeApiAtoms.tsx](apps/web/src/wsNativeApiAtoms.tsx), [wsRpcClient.ts](apps/web/src/wsRpcClient.ts), and all `*ReactQuery.ts` modules. |
15 | 15 |
|
16 | 16 | ## Phase 1: Infrastructure First |
17 | | -1. Extract the shared websocket RPC protocol layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) without changing behavior. |
| 17 | +1. Extract the shared websocket RPC protocol layer from [wsTransport.ts](apps/web/src/wsTransport.ts) without changing behavior. |
18 | 18 | 2. Build the AtomRpc client on top of that layer. |
19 | 19 | 3. Add one temporary `runRpc` helper for imperative handlers that still want `Promise` ergonomics; it must call the AtomRpc service directly and must not reintroduce a facade object. |
20 | 20 | 4. Replace manual registry wiring with one app-level registry provider based on `@effect/atom-react`. |
21 | 21 | 5. Land this as a no-behavior-change PR. |
22 | 22 |
|
23 | 23 | ## Phase 2: Replace `wsNativeApi`-Owned Push State |
24 | 24 | 1. Migrate welcome/config/provider/settings state first, because it is already atom-shaped and is the lowest-risk way to delete `wsNativeApi` responsibilities. |
25 | | -2. Replace [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts) with `rpc/serverState.ts`, updated directly from `subscribeServerLifecycle` and `subscribeServerConfig`. |
| 25 | +2. Replace [wsNativeApiState.ts](apps/web/src/wsNativeApiState.ts) with `rpc/serverState.ts`, updated directly from `subscribeServerLifecycle` and `subscribeServerConfig`. |
26 | 26 | 3. Keep the current hook names for one PR: `useServerConfig`, `useServerSettings`, `useServerProviders`, `useServerKeybindings`, `useServerWelcomeSubscription`, `useServerConfigUpdatedSubscription`. |
27 | | -4. Move bootstrap side effects out of [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx) into a new root bootstrap component mounted from [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx). |
28 | | -5. Delete the `server.getConfig()` fallback logic from [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts); snapshot fetch now lives beside the stream atoms. |
| 27 | +4. Move bootstrap side effects out of [wsNativeApiAtoms.tsx](apps/web/src/wsNativeApiAtoms.tsx) into a new root bootstrap component mounted from [__root.tsx](apps/web/src/routes/__root.tsx). |
| 28 | +5. Delete the `server.getConfig()` fallback logic from [wsNativeApi.ts](apps/web/src/wsNativeApi.ts); snapshot fetch now lives beside the stream atoms. |
29 | 29 |
|
30 | 30 | ## Phase 3: Replace React Query Domain By Domain |
31 | | -1. Replace [gitReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/gitReactQuery.ts) first. |
| 31 | +1. Replace [gitReactQuery.ts](apps/web/src/lib/gitReactQuery.ts) first. |
32 | 32 | 2. Add `rpc/gitAtoms.ts` and `rpc/useGit.ts` with `useGitStatus`, `useGitBranches`, `useResolvePullRequest`, and `useGitMutation`. |
33 | 33 | 3. Mutation settlement must invalidate scoped keys, not a global cache. `checkout`, `pull`, `init`, `createWorktree`, `removeWorktree`, `preparePullRequestThread`, and stacked actions invalidate `git:${cwd}`. Worktree create/remove also invalidates `project:${cwd}`. |
34 | | -4. Replace [projectReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/projectReactQuery.ts) second. `useProjectSearchEntries` must preserve current “keep previous results while loading” behavior. |
35 | | -5. Replace [providerReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/providerReactQuery.ts) third. Preserve current checkpoint error normalization and retry/backoff semantics inside the atom effect. Invalidate by `checkpoint:${threadId}`. |
| 34 | +4. Replace [projectReactQuery.ts](apps/web/src/lib/projectReactQuery.ts) second. `useProjectSearchEntries` must preserve current “keep previous results while loading” behavior. |
| 35 | +5. Replace [providerReactQuery.ts](apps/web/src/lib/providerReactQuery.ts) third. Preserve current checkpoint error normalization and retry/backoff semantics inside the atom effect. Invalidate by `checkpoint:${threadId}`. |
36 | 36 | 6. Defer the desktop updater until the last phase. |
37 | 37 |
|
38 | 38 | ## Phase 4: Move Root Invalidation Off `queryClient` |
39 | | -1. In [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx), remove `QueryClient` usage and replace the throttled `invalidateQueries` block with throttled invalidation helpers. |
| 39 | +1. In [__root.tsx](apps/web/src/routes/__root.tsx), remove `QueryClient` usage and replace the throttled `invalidateQueries` block with throttled invalidation helpers. |
40 | 40 | 2. Keep Zustand orchestration/event application unchanged. |
41 | 41 | 3. Map current effects exactly: |
42 | 42 | - git or checkpoint-affecting orchestration events touch `checkpoint:${threadId}` |
|
50 | 50 | - `rpc/gitActions.ts` |
51 | 51 | - `rpc/projectActions.ts` |
52 | 52 | - `platform/desktopBridge.ts` |
53 | | -2. Migrate direct [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) callers by domain, not file-by-file: git-heavy components first, then orchestration/thread actions, then shell/dialog helpers. |
54 | | -3. After the last caller is gone, delete [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) and the `window.nativeApi` fallback entirely. |
55 | | -4. In the final cleanup PR, remove `NativeApi` from [ipc.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/packages/contracts/src/ipc.ts) if nothing outside web still needs it. |
| 53 | +2. Migrate direct [nativeApi.ts](apps/web/src/nativeApi.ts) callers by domain, not file-by-file: git-heavy components first, then orchestration/thread actions, then shell/dialog helpers. |
| 54 | +3. After the last caller is gone, delete [nativeApi.ts](apps/web/src/nativeApi.ts) and the `window.nativeApi` fallback entirely. |
| 55 | +4. In the final cleanup PR, remove `NativeApi` from [ipc.ts](packages/contracts/src/ipc.ts) if nothing outside web still needs it. |
56 | 56 |
|
57 | 57 | ## Phase 6: Remove React Query Completely |
58 | 58 | 1. Delete `@tanstack/react-query` from `apps/web/package.json`. |
59 | | -2. Remove `QueryClientProvider` and router context from [router.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/router.ts) and [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx). |
60 | | -3. Replace [desktopUpdateReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/desktopUpdateReactQuery.ts) with a writable atom plus `desktopBridge.onUpdateState`. |
| 59 | +2. Remove `QueryClientProvider` and router context from [router.ts](apps/web/src/router.ts) and [__root.tsx](apps/web/src/routes/__root.tsx). |
| 60 | +3. Replace [desktopUpdateReactQuery.ts](apps/web/src/lib/desktopUpdateReactQuery.ts) with a writable atom plus `desktopBridge.onUpdateState`. |
61 | 61 | 4. Delete the old query-option tests. |
62 | 62 |
|
63 | 63 | ## Public Interfaces And Types |
|
70 | 70 | ## Test Plan |
71 | 71 | - Add unit tests for `rpc/serverState.ts`: snapshot bootstrapping, stream replay, provider/settings updates. |
72 | 72 | - Add unit tests for git/project/checkpoint hooks: loading, error mapping, retry behavior, invalidation, keep-previous-result behavior. |
73 | | -- Update the browser harness in [wsRpcHarness.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/test/wsRpcHarness.ts) to assert direct RPC + atom behavior instead of `__resetNativeApiForTests`. |
74 | | -- Replace [wsNativeApi.test.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.test.ts), `gitReactQuery.test.ts`, `providerReactQuery.test.ts`, and `desktopUpdateReactQuery.test.ts` with equivalent atom-backed coverage. |
| 73 | +- Update the browser harness in [wsRpcHarness.ts](apps/web/test/wsRpcHarness.ts) to assert direct RPC + atom behavior instead of `__resetNativeApiForTests`. |
| 74 | +- Replace [wsNativeApi.test.ts](apps/web/src/wsNativeApi.test.ts), `gitReactQuery.test.ts`, `providerReactQuery.test.ts`, and `desktopUpdateReactQuery.test.ts` with equivalent atom-backed coverage. |
75 | 75 | - Acceptance scenarios: |
76 | 76 | - welcome still bootstraps snapshot and navigation |
77 | 77 | - keybindings toast still responds to config stream updates |
|
0 commit comments