@terreno/mcp 0.31.0 → 56.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +8 -9
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  5. package/dist/docs/upgrades/56.0.0-beta.2.md +59 -0
  6. package/dist/docs/versioned/next/README.md +2 -0
  7. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
  8. package/dist/docs/versioned/next/how-to/README.md +2 -0
  9. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  10. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
  11. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
  12. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
  13. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
  14. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
  15. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
  16. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  17. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  18. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  19. package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  20. package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  21. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  22. package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  23. package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  24. package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  25. package/dist/docs/versioned/next/reference/README.md +2 -0
  26. package/dist/docs/versioned/next/reference/comms.md +132 -0
  27. package/dist/docs/versioned/next/reference/environment-variables.md +9 -0
  28. package/dist/docs/versioned/next/reference/syncdb.md +456 -0
  29. package/dist/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  30. package/dist/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  31. package/dist/docs/versioned/next/tasks/comms-abstraction.md +29 -16
  32. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
  33. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
  34. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
  35. package/dist/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  36. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  37. package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  38. package/dist/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  39. package/dist/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  40. package/package.json +3 -3
  41. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  42. package/src/docs/upgrades/56.0.0-beta.2.md +59 -0
  43. package/src/docs/versioned/next/README.md +2 -0
  44. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
  45. package/src/docs/versioned/next/how-to/README.md +2 -0
  46. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  47. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
  48. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
  49. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
  50. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
  51. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
  52. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
  53. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  54. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  55. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  56. package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  57. package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  58. package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  59. package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  60. package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  61. package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  62. package/src/docs/versioned/next/reference/README.md +2 -0
  63. package/src/docs/versioned/next/reference/comms.md +132 -0
  64. package/src/docs/versioned/next/reference/environment-variables.md +9 -0
  65. package/src/docs/versioned/next/reference/syncdb.md +456 -0
  66. package/src/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  67. package/src/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  68. package/src/docs/versioned/next/tasks/comms-abstraction.md +29 -16
  69. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
  70. package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
  71. package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
  72. package/src/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  73. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  74. package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  75. package/src/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  76. package/src/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  77. package/dist/__tests__/bootstrap.test.d.ts +0 -2
  78. package/dist/__tests__/bootstrap.test.d.ts.map +0 -1
  79. package/dist/__tests__/bootstrap.test.js +0 -299
  80. package/dist/__tests__/bootstrap.test.js.map +0 -1
  81. package/dist/__tests__/databaseQuery.test.d.ts +0 -2
  82. package/dist/__tests__/databaseQuery.test.d.ts.map +0 -1
  83. package/dist/__tests__/databaseQuery.test.js +0 -83
  84. package/dist/__tests__/databaseQuery.test.js.map +0 -1
  85. package/dist/__tests__/docsRoot.test.d.ts +0 -2
  86. package/dist/__tests__/docsRoot.test.d.ts.map +0 -1
  87. package/dist/__tests__/docsRoot.test.js +0 -59
  88. package/dist/__tests__/docsRoot.test.js.map +0 -1
  89. package/dist/__tests__/inferPackages.test.d.ts +0 -2
  90. package/dist/__tests__/inferPackages.test.d.ts.map +0 -1
  91. package/dist/__tests__/inferPackages.test.js +0 -35
  92. package/dist/__tests__/inferPackages.test.js.map +0 -1
  93. package/dist/__tests__/preload.d.ts +0 -2
  94. package/dist/__tests__/preload.d.ts.map +0 -1
  95. package/dist/__tests__/preload.js +0 -12
  96. package/dist/__tests__/preload.js.map +0 -1
  97. package/dist/__tests__/prompts.test.d.ts +0 -2
  98. package/dist/__tests__/prompts.test.d.ts.map +0 -1
  99. package/dist/__tests__/prompts.test.js +0 -295
  100. package/dist/__tests__/prompts.test.js.map +0 -1
  101. package/dist/__tests__/resources.test.d.ts +0 -2
  102. package/dist/__tests__/resources.test.d.ts.map +0 -1
  103. package/dist/__tests__/resources.test.js +0 -352
  104. package/dist/__tests__/resources.test.js.map +0 -1
  105. package/dist/__tests__/search.test.d.ts +0 -2
  106. package/dist/__tests__/search.test.d.ts.map +0 -1
  107. package/dist/__tests__/search.test.js +0 -163
  108. package/dist/__tests__/search.test.js.map +0 -1
  109. package/dist/__tests__/tools.test.d.ts +0 -2
  110. package/dist/__tests__/tools.test.d.ts.map +0 -1
  111. package/dist/__tests__/tools.test.js +0 -540
  112. package/dist/__tests__/tools.test.js.map +0 -1
  113. package/dist/__tests__/upgradeGuide.test.d.ts +0 -2
  114. package/dist/__tests__/upgradeGuide.test.d.ts.map +0 -1
  115. package/dist/__tests__/upgradeGuide.test.js +0 -22
  116. package/dist/__tests__/upgradeGuide.test.js.map +0 -1
@@ -0,0 +1,445 @@
1
+ # How to Migrate from @terreno/rtk to @terreno/syncdb
2
+
3
+ This guide is **AI-context-first**: it is written for an agent (or human) performing a screen-by-screen migration, not as a codemod. Work through one concern at a time — reads, then writes, then conflicts, then sync-status UX — and delete RTK-era code as you go.
4
+
5
+ `@terreno/syncdb` supersedes `@terreno/rtk` for **data synchronization** (CRUD reads/writes, offline queue, realtime convergence). It does not replace the generated OpenAPI SDK, Better Auth Redux wiring, or feature-flag infrastructure.
6
+
7
+ ## 1. Before you start
8
+
9
+ | Decision | Requirement | Why |
10
+ |----------|-------------|-----|
11
+ | **Better Auth first?** | Recommended before or in parallel with data migration | `betterAuthAdapter` is the shipped `AuthProvider`; socket sessions work cleanly with `RealtimeApp({betterAuth})`. Legacy JWT sockets still work during transition. |
12
+ | **Backend version** | `@terreno/api` with `SyncApp`, `RealtimeApp`, `syncPlugin`, `isDeletedPlugin` | Without backend sync registration, the client has nothing to talk to. |
13
+ | **MongoDB replica set** | Required for `RealtimeApp` change streams | Single-node replset is fine for dev. |
14
+ | **Per-collection rollout** | One synced collection at a time | REST and sync share the same write path; migrate screen-by-screen. |
15
+ | **Redux** | Keep Redux for auth, feature flags, app state; drop RTK Query for migrated collections | See §2. |
16
+
17
+ **Auth can migrate before data (M3).** A typical two-step order:
18
+
19
+ 1. **Step A — Auth:** Move login/session to Better Auth (`generateBetterAuthSlice`, `betterAuthAdapter`, `syncDb.start()` on login). Screens can still read/write via RTK Query during this step.
20
+ 2. **Step B — Data:** For each collection, replace RTK Query hooks with syncdb hooks and delete the RTK path for that screen.
21
+
22
+ The example app completed both steps; todos are syncdb-only while profile/admin still use generated RTK hooks.
23
+
24
+ Reference: [`docs/reference/syncdb.md`](../reference/syncdb.md), and the package [`syncdb/README.md`](https://github.com/flourishhealth/terreno/blob/release-56.0.0/syncdb/README.md).
25
+
26
+ ## 2. Install and configure
27
+
28
+ ### Install packages
29
+
30
+ ```bash
31
+ bun install @terreno/syncdb
32
+ bunx expo install expo-sqlite # native persistence — install in the app, rebuild native
33
+ ```
34
+
35
+ ### Create the sync client
36
+
37
+ **After** (`example-frontend/store/syncdb.ts`):
38
+
39
+ ```typescript
40
+ import {baseUrl} from "@terreno/rtk";
41
+ import {betterAuthAdapter, createSyncDb} from "@terreno/syncdb";
42
+ import {betterAuthClient} from "@/lib/betterAuth";
43
+
44
+ const authProvider = betterAuthAdapter(betterAuthClient, {pollIntervalMs: 60_000});
45
+
46
+ export const syncDb = createSyncDb({
47
+ authProvider,
48
+ baseUrl,
49
+ collections: ["todos"],
50
+ name: "terreno-example",
51
+ debug: __DEV__ ? {capacity: 1000} : false,
52
+ });
53
+ ```
54
+
55
+ ### Start/stop on auth
56
+
57
+ **After** (`example-frontend/app/_layout.tsx`):
58
+
59
+ ```typescript
60
+ useEffect(() => {
61
+ if (!userId) {
62
+ return;
63
+ }
64
+ let stopped = false;
65
+ syncDb
66
+ .start()
67
+ .then(() => {
68
+ if (!stopped) {
69
+ setSyncDbReady(true);
70
+ }
71
+ })
72
+ .catch((error) => {
73
+ console.error("[syncdb] Failed to start client", error);
74
+ });
75
+ return () => {
76
+ stopped = true;
77
+ setSyncDbReady(false);
78
+ void syncDb.stop();
79
+ };
80
+ }, [userId, syncDbStartAttempt]);
81
+ ```
82
+
83
+ Gate writes with a ready flag (`useSyncDbReady`) so `mutate()` is not called before `start()` resolves a user.
84
+
85
+ ### Wrap screens with `SyncDbProvider`
86
+
87
+ **After** (`example-frontend/app/(tabs)/index.tsx`):
88
+
89
+ ```tsx
90
+ import {SyncDbProvider} from "@terreno/syncdb/react";
91
+ import {syncDb} from "@/store/syncdb";
92
+
93
+ const TodosScreen = () => (
94
+ <SyncDbProvider client={syncDb}>
95
+ <SyncTodosScreen />
96
+ </SyncDbProvider>
97
+ );
98
+ ```
99
+
100
+ The example also mounts `SyncDbProvider` at the root (`_layout.tsx`) for `SyncHealthToast`.
101
+
102
+ ### Redux store: keep vs remove
103
+
104
+ | Keep on Redux | Remove for migrated collections |
105
+ |---------------|--------------------------------|
106
+ | `generateBetterAuthSlice` / session | `generateAuthSlice` (JWT) once Better Auth ships |
107
+ | `terrenoApi` reducer for **non-synced** routes | `useGetTodosQuery` / todo mutations |
108
+ | `createOfflineMiddleware` + offline slice | `realtimeList` / `realtimeDocument` for that collection |
109
+ | Feature-flag / app-state slices | Per-screen `isLoading` spinners tied to RTK mutations |
110
+
111
+ **Before** (`origin/master:example-frontend/store/index.ts`): `generateAuthSlice`, `createOfflineMiddleware`, offline payload sessionStorage.
112
+
113
+ **After** (`example-frontend/store/index.ts`): `generateBetterAuthSlice`, `terrenoApi` only — no offline middleware.
114
+
115
+ ## 3. Reads
116
+
117
+ There is no `isLoading` for reads after bootstrap — data is already in the local store.
118
+
119
+ ### List reads
120
+
121
+ **Before** (`origin/master:example-frontend/app/(tabs)/index.tsx`):
122
+
123
+ ```typescript
124
+ const {data: todosData, isLoading, refetch, isFetching} = useGetTodosQuery({}, {skip: !userId});
125
+ const todos = todosData?.data ?? [];
126
+ ```
127
+
128
+ **After** (`example-frontend/components/SyncTodosScreen.tsx`):
129
+
130
+ ```typescript
131
+ import {useEntity, useEntityIds} from "@terreno/syncdb/react";
132
+
133
+ const incompleteIds = useEntityIds<SyncTodo>("todos", {
134
+ filter: (t) => !t.completed,
135
+ sort: sortByCreatedDesc,
136
+ });
137
+
138
+ // Per row:
139
+ const {data} = useEntity<SyncTodo>("todos", id);
140
+ ```
141
+
142
+ For small lists, `useQuery` is fine:
143
+
144
+ ```typescript
145
+ import {useQuery} from "@terreno/syncdb/react";
146
+ const todos = useQuery<Todo>("todos", {filter: (t) => !t.completed});
147
+ ```
148
+
149
+ `filter` / `sort` run in JS — there are no server query params. Tombstones are excluded unless `{includeDeleted: true}`.
150
+
151
+ ### Delete this RTK read code
152
+
153
+ - `useGetTodosQuery` / `useGet*Query` for the migrated collection
154
+ - `isLoading`, `isFetching`, `refetch`, `RefreshControl` wired to list fetch
155
+ - `skip: !userId` guards on list queries (replace with `useSyncDbReady` on writes; reads are safe once bootstrapped)
156
+ - `realtimeList("todos")` / `realtimeDocument("todos")` in `store/sdk.ts` for that collection
157
+
158
+ ## 4. Writes
159
+
160
+ Writes are synchronous from the UI's perspective: local apply + outbox enqueue + background replay.
161
+
162
+ ### Create / update / delete
163
+
164
+ **Before** (`origin/master:example-frontend/app/(tabs)/index.tsx`):
165
+
166
+ ```typescript
167
+ const [createTodo, {isLoading: isCreating}] = usePostTodosMutation();
168
+ const [updateTodo] = usePatchTodosByIdMutation();
169
+ const [deleteTodo] = useDeleteTodosByIdMutation();
170
+
171
+ await createTodo({title, completed: false}).unwrap();
172
+ await updateTodo({id, completed: true}).unwrap();
173
+ await deleteTodo(id).unwrap();
174
+ ```
175
+
176
+ **After** (`example-frontend/components/SyncTodosScreen.tsx`):
177
+
178
+ ```typescript
179
+ import {generateMutationId} from "@terreno/syncdb";
180
+ import {useMutate} from "@terreno/syncdb/react";
181
+
182
+ const {create, update, remove} = useMutate("todos");
183
+
184
+ const id = generateMutationId();
185
+ create({data: {_id: id, title, completed: false, created: DateTime.now().toISO()}});
186
+ update({id, data: {completed: true}});
187
+ remove({id});
188
+ ```
189
+
190
+ Client-minted `_id` on creates lets the optimistic row render before the server acks.
191
+
192
+ ### Delete this RTK write code
193
+
194
+ - `usePost*Mutation`, `usePatch*Mutation`, `useDelete*Mutation` for the migrated collection
195
+ - `.unwrap()` / try-catch around mutation promises for happy-path UX
196
+ - **`isLoading` / `isCreating` / `isUpdating` spinners on write buttons** — syncdb writes do not await the server
197
+ - Manual optimistic cache updates (`updateQueryData`, `upsertQueryData`, temporary local rows in component state)
198
+ - `refetch()` or tag invalidation after mutate (`invalidatesTags` for that collection)
199
+ - `createOfflineMiddleware` endpoint list entries for that collection (`configureOfflineMutationEndpoints`)
200
+ - `If-Unmodified-Since` / 409 handling in custom mutation code (conflicts move to `useConflicts()`)
201
+
202
+ Server rejection surfaces later as outbox nacks — conflicts in `useConflicts()`, terminal failures in `getSyncStatus().failedCount`.
203
+
204
+ ## 5. Conflicts
205
+
206
+ A **conflict** means the client's `baseVersion` (last seen `_syncSeq`) disagrees with the server. The server returns a `conflict` nack:
207
+
208
+ - HTTP: `POST /sync/mutate` → **409** with `{nack: {code: "conflict", serverDoc, serverSeq, ...}}`
209
+ - Socket: `sync:nack` with `code: "conflict"`
210
+
211
+ The client records the conflict locally; the UI must let the user pick a side.
212
+
213
+ ### Strategies
214
+
215
+ | Strategy | When to use |
216
+ |----------|-------------|
217
+ | `useServer` | Accept canonical server data (or tombstone if server deleted the doc) |
218
+ | `keepMine` | Re-enqueue local changes with a fresh `baseVersion` |
219
+
220
+ ```typescript
221
+ import {useConflicts} from "@terreno/syncdb/react";
222
+
223
+ const {conflicts, resolve} = useConflicts();
224
+ resolve({mutationId, strategy: "useServer"});
225
+ resolve({mutationId, strategy: "keepMine"});
226
+ ```
227
+
228
+ ### Minimum UI
229
+
230
+ The example app uses:
231
+
232
+ - `SyncConflictsController` — single app-wide sheet state (`example-frontend/components/SyncConflictsController.tsx`)
233
+ - `ConflictSheet` rendered from `SyncHealthToast` (`example-frontend/app/_layout.tsx`)
234
+ - `SyncStatusBanner` conflict badge → `openConflicts("todos")` (`example-frontend/components/SyncTodosScreen.tsx`)
235
+
236
+ Copy that pattern or build equivalent: show local vs server fields, two resolution actions, block further edits on conflicted entities until resolved.
237
+
238
+ ## 6. Sync status
239
+
240
+ Replace **request-loading UX** with **sync-state UX**.
241
+
242
+ | RTK era | Syncdb era |
243
+ |---------|------------|
244
+ | `isLoading` on queries | No list loading spinner (data is local) |
245
+ | `useOfflineStatus` / `useServerStatus` | `useSyncStatus()` |
246
+ | `OfflineBanner` on fetch failure | `SyncStatusBanner` + `SyncHealthToast` |
247
+ | Pull-to-refresh `refetch()` | Optional `client.forceResync()` for support tooling |
248
+
249
+ **After** (`example-frontend/components/SyncTodosScreen.tsx`):
250
+
251
+ ```typescript
252
+ import {useSyncStatus} from "@terreno/syncdb/react";
253
+ import {SyncStatusBanner} from "@terreno/ui";
254
+
255
+ const syncStatus = useSyncStatus();
256
+
257
+ <SyncStatusBanner
258
+ isOnline={syncStatus.isOnline}
259
+ queuedCount={syncStatus.queuedCount}
260
+ conflictCount={syncStatus.conflictCount}
261
+ failedCount={syncStatus.failedCount}
262
+ paused={syncStatus.paused}
263
+ draining={syncStatus.draining}
264
+ sentThisDrain={syncStatus.sentThisDrain}
265
+ totalThisDrain={syncStatus.totalThisDrain}
266
+ onOpenConflicts={openConflictSheet}
267
+ onAuthRequired={handleAuthRequired}
268
+ />
269
+ ```
270
+
271
+ `SyncHealthToast` (`example-frontend/components/SyncHealthToast.tsx`) adds persistent toasts for per-collection conflicts/failures and an initial "Syncing…" toast with optional force reload.
272
+
273
+ `paused: "auth"` means replay is halted until the same user re-authenticates — wire `onAuthRequired` to your login flow.
274
+
275
+ ## 7. Auth
276
+
277
+ **Auth can ship before data migration.** Complete Better Auth + `betterAuthAdapter` first; keep RTK Query for reads/writes until each screen migrates.
278
+
279
+ ### Better Auth adapter
280
+
281
+ **After** (`example-frontend/store/syncdb.ts`):
282
+
283
+ ```typescript
284
+ import {betterAuthAdapter, createSyncDb} from "@terreno/syncdb";
285
+ import {betterAuthClient} from "@/lib/betterAuth";
286
+
287
+ const authProvider = betterAuthAdapter(syncAuthClient, {pollIntervalMs: 60_000});
288
+
289
+ export const syncDb = createSyncDb({authProvider, baseUrl, collections: ["todos"], name: "terreno-example"});
290
+ ```
291
+
292
+ `betterAuthAdapter` implements `AuthProvider`:
293
+
294
+ - `getToken()` / `getUserId()` — read session per request (not cached)
295
+ - `onAuthChange` — session atom subscription (example bridges `$store.atoms.session`)
296
+ - `refresh()` — one silent refresh per auth-pause episode
297
+
298
+ ### Token storage
299
+
300
+ Better Auth session tokens live in the Better Auth client (`@terreno/rtk` `createBetterAuthClient`):
301
+
302
+ - **Native:** `expo-secure-store`
303
+ - **Web:** `@react-native-async-storage/async-storage`
304
+
305
+ Syncdb does not store tokens itself — it calls `authProvider` on every transport/HTTP request.
306
+
307
+ ### Socket auth and `sync:auth-expired`
308
+
309
+ On connect, the socket transport sends the bearer token from `getToken()`. When the server's session re-validation sweep finds an expired/disabled session, it emits `sync:auth-expired` then disconnects (`api/src/realtime/sessionRevalidation.ts`). The client maps this to `paused: "auth"` — **no local wipe**, outbox preserved (INV-2).
310
+
311
+ ### Backend
312
+
313
+ ```typescript
314
+ new RealtimeApp({betterAuth: {auth, userModel: User}})
315
+ ```
316
+
317
+ Required for Better Auth socket sessions. Legacy JWT sockets continue to work without it.
318
+
319
+ ## 8. Codegen
320
+
321
+ ### Does `bun run sdk` still work?
322
+
323
+ **Yes.** `example-frontend/package.json` still defines:
324
+
325
+ ```json
326
+ "sdk": "bun scripts/generate-sdk.ts && bun biome check --write scripts/generate-sdk.ts"
327
+ ```
328
+
329
+ It runs `@rtk-query/codegen-openapi` against `openapi-config.ts` and writes `store/openApiSdk.ts`.
330
+
331
+ ### What changes post-migration
332
+
333
+ | Still generated | Stop using for migrated collections |
334
+ |-----------------|-------------------------------------|
335
+ | Auth routes, `GET /auth/me`, admin, AI explorer, version config, feature flags | `useGetTodosQuery`, `usePostTodosMutation`, etc. |
336
+ | Custom REST / RPC endpoints | Todo `realtimeList` / `realtimeDocument` wiring in `store/sdk.ts` |
337
+
338
+ Keep running `bun run sdk` after backend route changes — non-synced screens still import from `store/sdk.ts`.
339
+
340
+ Workflow:
341
+
342
+ ```bash
343
+ # Backend running on port 4000
344
+ cd example-frontend && bun run sdk
345
+ ```
346
+
347
+ ## 9. Feature flags
348
+
349
+ Post-migration, feature flags stay on `@terreno/rtk` + OpenFeature — **not** on syncdb.
350
+
351
+ **After** (`example-frontend/app/_layout.tsx`):
352
+
353
+ ```typescript
354
+ import {OpenFeatureProvider} from "@openfeature/react-sdk";
355
+ import {useTerrenoFeatureFlags} from "@terreno/rtk";
356
+
357
+ const OpenFeatureBridge = ({children, socket}) => {
358
+ const bridgeUserId = useSelector(selectBetterAuthUserId) ?? undefined;
359
+ useTerrenoFeatureFlags(terrenoApi, {skip: !bridgeUserId, socket, userId: bridgeUserId});
360
+ return <OpenFeatureProvider domain="feature-flags">{children}</OpenFeatureProvider>;
361
+ };
362
+ ```
363
+
364
+ See [`docs/reference/feature-flags.md`](../reference/feature-flags.md) and [`docs/how-to/add-feature-flags.md`](add-feature-flags.md).
365
+
366
+ **Example app gap:** todos do **not** gate on a feature flag — `SyncTodosScreen` is syncdb-only. For a gradual rollout, add a boolean flag and render the RTK screen when off, syncdb screen when on (see §10). The reference migration intentionally skipped flag wiring to keep one data path for e2e tests.
367
+
368
+ ## 10. Rollback
369
+
370
+ ### Per-screen feature flag
371
+
372
+ Ship both implementations behind a flag:
373
+
374
+ - Flag **off** → legacy RTK Query screen
375
+ - Flag **on** → syncdb screen
376
+
377
+ Both bundles ship until you flip the default and delete the RTK path.
378
+
379
+ The example app does **not** use this pattern for todos.
380
+
381
+ ### What local data survives
382
+
383
+ | Event | Local IndexedDB / SQLite | Outbox |
384
+ |-------|------------------------|--------|
385
+ | Logout / 401 | **Kept** (INV-2) | **Kept** — same user can sign back in and replay |
386
+ | Different-user login | **Wiped** | **Wiped** |
387
+ | `signOut()` default | **Kept** | **Kept** |
388
+ | `signOut()` with `wipeOnSignOut: true` | **Wiped** | **Wiped** |
389
+ | Schema version mismatch | **Wiped** + re-bootstrap | Re-built |
390
+ | Decrypt failure (web, default) | **Wiped** + re-bootstrap | Re-built |
391
+
392
+ `wipeLocalData` clears store tables, persisted databases, and cached encryption keys (`syncdb/src/storage/wipe.ts`).
393
+
394
+ Rolling back a **screen** to RTK does not automatically clear syncdb data — stale local todos may still exist until user switch or explicit wipe. Plan for that if you flip the flag off in production.
395
+
396
+ ## 11. Checklist
397
+
398
+ Copy and track per collection:
399
+
400
+ ```
401
+ [ ] 1. Backend: isDeletedPlugin + syncPlugin on schema
402
+ [ ] 2. Backend: modelRouter sync scope configured
403
+ [ ] 3. Backend: SyncApp + RealtimeApp registered; Mongo replica set running
404
+ [ ] 4. Backend: ensureSyncIndexes succeeds at startup
405
+ [ ] 5. Frontend: @terreno/syncdb installed; expo-sqlite in app (native)
406
+ [ ] 6. Frontend: createSyncDb client + betterAuthAdapter
407
+ [ ] 7. Frontend: syncDb.start()/stop() wired to auth in root layout
408
+ [ ] 8. Frontend: SyncDbProvider wraps migrated screens
409
+ [ ] 9. Reads: useGet*Query → useQuery / useEntityIds + useEntity
410
+ [ ] 10. Writes: usePost/Patch/Delete*Mutation → useMutate().create/update/remove
411
+ [ ] 11. Deleted RTK: mutation isLoading spinners, refetch-after-mutate, offline middleware entries, realtime cache patches
412
+ [ ] 12. Conflicts: useConflicts + resolution UI (ConflictSheet or equivalent)
413
+ [ ] 13. Sync status: useSyncStatus + SyncStatusBanner / health toasts (not query isLoading)
414
+ [ ] 14. Auth: Better Auth session + onAuthRequired → re-login when paused
415
+ [ ] 15. Codegen: bun run sdk still run for non-synced routes; remove todo hooks from screens
416
+ [ ] 16. Feature flags: OpenFeature bridge still fed via useTerrenoFeatureFlags (if used)
417
+ [ ] 17. Rollback plan: feature flag or accept stale local data on revert
418
+ [ ] 18. Verify: CRUD, offline create/edit/delete, reconnect, conflict both strategies, user switch
419
+ ```
420
+
421
+ ## Backend prerequisites (detail)
422
+
423
+ Before a collection can sync:
424
+
425
+ - [ ] `isDeletedPlugin` on schema (soft delete required)
426
+ - [ ] `syncPlugin` on schema (`_syncSeq` stamping)
427
+ - [ ] `modelRouter("/path", Model, {sync: {scope: ...}})` three-argument form
428
+ - [ ] Custom scopes include `snapshotFilter`
429
+ - [ ] `new SyncApp({getUserScopes?})` for tenant/custom scopes
430
+ - [ ] `new RealtimeApp({betterAuth?})` + MongoDB replica set
431
+ - [ ] No `updateMany`, `deleteMany`, `deleteOne`, `findOneAndDelete`, or `bulkWrite` on synced models
432
+
433
+ ## What stays on @terreno/rtk
434
+
435
+ - Generated OpenAPI SDK for non-synced routes
436
+ - Better Auth Redux slice (`generateBetterAuthSlice`)
437
+ - Feature flags (`useTerrenoFeatureFlags` / `useFeatureFlags`)
438
+ - Socket connection for flags and legacy realtime (until removed)
439
+ - `@terreno/rtk` is not being removed
440
+
441
+ ## Further reading
442
+
443
+ - [`docs/reference/syncdb.md`](../reference/syncdb.md) — full API reference
444
+ - [`syncdb/README.md`](https://github.com/flourishhealth/terreno/blob/release-56.0.0/syncdb/README.md) — architecture and protocol detail
445
+ - [`docs/how-to/configure-better-auth.md`](configure-better-auth.md) — Better Auth setup
@@ -9,35 +9,47 @@ Umbrella plans that coordinate several IPs.
9
9
  | Program | Status | Created | IPs |
10
10
  |---------|--------|---------|-----|
11
11
  | [Open source launch](oss-launch-program.md) | Draft — decisions recorded; Vercel spike open | 2026-07-27 | 15 |
12
- | [B2B platform](b2b-platform-program.md) | Draft — decisions D1–D7 resolved | 2026-08-09 | 9 written (+ [rbac-permissions](rbac-permissions.md)); 29 roadmap items seeded |
13
12
 
14
13
  ## Active
15
14
 
16
15
  | Plan | Status | Created | Tasks |
17
16
  |------|--------|---------|-------|
17
+ | [Migrate CI/CD to CircleCI](migrate-cicd-to-circleci.md) | Draft — decisions recorded | 2026-08-17 | [tasks](../tasks/migrate-cicd-to-circleci.md) |
18
18
  | [Admin UI v2 — Django-parity admin](admin-ui-v2-django-parity.md) | Approved | 2026-06-15 | [tasks](../tasks/admin-ui-v2-django-parity.md) |
19
- | [RBAC Permissions](rbac-permissions.md) | Draft (API design) | 2026-07-08 | TBD |
20
- | [SyncDB Local-First Data Layer](syncdb-local-first.md) | In progress ([PR #869](https://github.com/flourishhealth/terreno/pull/869)) | 2026-07-13 | [tasks](../tasks/syncdb-local-first.md) |
21
- | [MCP Boost parity](mcp-boost-parity.md) | In progress ([PR #802](https://github.com/flourishhealth/terreno/pull/802)) | 2026-06-21 | TBD |
22
- | [Infrastructure MCP server](infra-mcp.md) | Draft — blocked on [RBAC](rbac-permissions.md) | 2026-08-09 | [tasks](../tasks/infra-mcp.md) |
19
+ | [@terreno/syncdb-codegen](syncdb-codegen.md) | Draft | 2026-07-08 | [tasks](../tasks/syncdb-codegen.md) |
20
+ | [SyncDB local-first data layer (v2)](syncdb-local-first.md) | In progress — Phases 1–8 landed, Phase 9 follow-ups open | 2026-06-23 | [tasks](../tasks/syncdb-local-first.md) |
21
+ | [SyncDB hardening (syncdb-2)](terreno-syncdb-2.md) | Implemented — Phases A–F landed; deviations carried into Phase 9 | 2026-07-11 | [tasks](../tasks/syncdb-local-first.md) (Phase 8/9) |
22
+ | [SyncDB Phase C design](syncdb-phase-c-design.md) | Implemented — design authority for C1–C8 | 2026-07-11 | [tasks](../tasks/syncdb-local-first.md) (Phase 8) |
23
+
24
+ ## Completed
25
+
26
+ | Plan | Status | Created | Tasks |
27
+ |------|--------|---------|-------|
28
+ | [APIError redesign — standards-first extension of Error](apierror-standard-error-redesign.md) | Complete | 2026-07-28 | n/a |
23
29
 
24
- ## Open source launch IPs
30
+ ## Deferred / Closed
25
31
 
26
- All part of the [launch program](oss-launch-program.md). The **RTK flag** column marks dependence on the `@terreno/rtk` → `@terreno/syncdb` migration in PR #869: `None` is safe to implement now, `Partial` has some blocked tasks, `Blocked` cannot start until #869 merges.
32
+ *(Plans parked or superseded.)*
27
33
 
28
- ### Wave 0 — unblocked
34
+ ## Backlog
29
35
 
30
- | Plan | Status | RTK flag | Tasks |
31
- |------|--------|----------|-------|
36
+ *(Links to ideas not yet promoted to a full IP.)*
37
+
38
+ | Idea | Notes |
39
+ |------|-------|
40
+ | [Langfuse integration](terreno-langfuse-integration.md) | AI observability plugin |
41
+ | [modelRouter MCP tools](model-router-mcp.md) | MCP surface for consumer app APIs |
42
+ | [Offline mode](offline-mode.md) | Superseded in large part by [syncdb](syncdb-local-first.md) — confirm and close |
43
+ | [modelRouter actions](model-router-actions.md) | — |
44
+ | [B2B platform](b2b-platform-program.md) | Draft — decisions D1–D7 resolved | 2026-08-09 | 10 written (+ [rbac-permissions](rbac-permissions.md)); 30 roadmap items seeded |
45
+ | [RBAC Permissions](rbac-permissions.md) | Draft (API design) | 2026-07-08 | TBD |
46
+ | [SyncDB Local-First Data Layer](syncdb-local-first.md) | In progress ([PR #869](https://github.com/flourishhealth/terreno/pull/869)) | 2026-07-13 | [tasks](../tasks/syncdb-local-first.md) |
47
+ | [MCP Boost parity](mcp-boost-parity.md) | In progress ([PR #802](https://github.com/flourishhealth/terreno/pull/802)) | 2026-06-21 | TBD |
48
+ | [Infrastructure MCP server](infra-mcp.md) | Draft — blocked on [RBAC](rbac-permissions.md) | 2026-08-09 | [tasks](../tasks/infra-mcp.md) |
32
49
  | [OSS governance baseline](oss-governance-baseline.md) | Draft | None | [tasks](../tasks/oss-governance-baseline.md) |
33
50
  | [Public roadmap on GitHub](public-roadmap-github.md) | Draft | None | [tasks](../tasks/public-roadmap-github.md) |
34
51
  | [Deploy to GCP (generalized)](deploy-to-gcp.md) | Draft | None | [tasks](../tasks/deploy-to-gcp.md) |
35
52
  | [Agentic SDLC plugin (`/terreno-*`)](agentic-sdlc-plugin.md) | Draft | Partial | [tasks](../tasks/agentic-sdlc-plugin.md) |
36
-
37
- ### Wave 1 — gated on PR #869
38
-
39
- | Plan | Status | RTK flag | Tasks |
40
- |------|--------|----------|-------|
41
53
  | [RTK deprecation and syncdb migration docs](rtk-to-syncdb-migration-docs.md) | Draft | **Blocked** (gating IP) | [tasks](../tasks/rtk-to-syncdb-migration-docs.md) |
42
54
  | [Positioning — Django/Rails for TypeScript](positioning-django-rails-universal.md) | Draft | Partial | [tasks](../tasks/positioning-django-rails-universal.md) |
43
55
  | [Reference documentation coverage](docs-reference-coverage.md) | Draft | Blocked | [tasks](../tasks/docs-reference-coverage.md) |
@@ -48,48 +60,14 @@ All part of the [launch program](oss-launch-program.md). The **RTK flag** column
48
60
  | [The AI development loop (Boost)](ai-dev-loop-boost.md) | Draft | Partial | [tasks](../tasks/ai-dev-loop-boost.md) |
49
61
  | [Dogfooding run and launch blog post](build-terreno-app-validation.md) | Draft | Blocked | [tasks](../tasks/build-terreno-app-validation.md) |
50
62
  | [Examples, demo, and test coverage](examples-demo-coverage.md) | Draft | Partial | [tasks](../tasks/examples-demo-coverage.md) |
51
-
52
- ### Wave 2 — independent
53
-
54
- | Plan | Status | RTK flag | Tasks |
55
- |------|--------|----------|-------|
56
63
  | [Web SSR and admin SPA](web-ssr-and-admin-spa.md) | Draft | Partial | [tasks](../tasks/web-ssr-and-admin-spa.md) |
57
-
58
- ## B2B platform IPs
59
-
60
- All part of the [B2B platform program](b2b-platform-program.md). Roadmap items for the
61
- full program (including items whose IPs are pending decisions D1–D4) are seeded in
62
- [roadmap-seed-issues.md](../explanation/roadmap-seed-issues.md).
63
-
64
- | Plan | Status | RTK flag | Tasks |
65
- |------|--------|----------|-------|
66
64
  | [Pluggable communications layer (@terreno/comms)](comms-abstraction.md) | Draft | None | [tasks](../tasks/comms-abstraction.md) |
67
65
  | [Comms adapter — Expo push](comms-adapter-expo-push.md) | Draft | None | [tasks](../tasks/comms-adapter-expo-push.md) |
68
66
  | [Comms adapter — Twilio SMS](comms-adapter-twilio-sms.md) | Draft | None | [tasks](../tasks/comms-adapter-twilio-sms.md) |
69
67
  | [Comms adapter — Twilio Verify (OTP)](comms-adapter-twilio-verify.md) | Draft | None | [tasks](../tasks/comms-adapter-twilio-verify.md) |
70
68
  | [Comms adapter — SendGrid email](comms-adapter-sendgrid.md) | Draft | None | [tasks](../tasks/comms-adapter-sendgrid.md) |
69
+ | [Comms admin dashboard (errors, retries, log digging)](comms-admin-dashboard.md) | Draft | Partial | [tasks](../tasks/comms-admin-dashboard.md) |
71
70
  | [Password reset and email verification](password-reset-and-email-verification.md) | Draft | None | [tasks](../tasks/password-reset-and-email-verification.md) |
72
71
  | [Organizations, teams, and multi-tenant scoping](orgs-and-teams.md) | Draft | None | [tasks](../tasks/orgs-and-teams.md) |
73
72
  | [Stripe billing and subscriptions](billing-stripe.md) | Draft | None | [tasks](../tasks/billing-stripe.md) |
74
73
  | [Native module baseline (next major)](native-module-baseline.md) | Draft | None | [tasks](../tasks/native-module-baseline.md) |
75
-
76
- ## Completed
77
-
78
- | Plan | Status | Created | Tasks |
79
- |------|--------|---------|-------|
80
- | [APIError redesign — standards-first extension of Error](apierror-standard-error-redesign.md) | Complete | 2026-07-28 | n/a |
81
-
82
- ## Deferred / Closed
83
-
84
- *(Plans parked or superseded.)*
85
-
86
- ## Backlog
87
-
88
- *(Links to ideas not yet promoted to a full IP.)*
89
-
90
- | Idea | Notes |
91
- |------|-------|
92
- | [Langfuse integration](terreno-langfuse-integration.md) | AI observability plugin |
93
- | [modelRouter MCP tools](model-router-mcp.md) | MCP surface for consumer app APIs |
94
- | [Offline mode](offline-mode.md) | Superseded in large part by [syncdb](syncdb-local-first.md) — confirm and close |
95
- | [modelRouter actions](model-router-actions.md) | — |
@@ -1,6 +1,6 @@
1
1
  # Program: B2B platform
2
2
 
3
- **Status:** Draft — roadmap items drafted; decisions D1–D7 resolved 2026-08-09; first nine IPs open
3
+ **Status:** Draft — roadmap items drafted; decisions D1–D7 resolved 2026-08-09; first ten IPs open
4
4
  **Owner:** unassigned
5
5
  **Created:** 2026-08-09
6
6
 
@@ -32,7 +32,7 @@ org switcher, dark mode).
32
32
  | Track | Theme | IPs / roadmap items |
33
33
  |---|---|---|
34
34
  | A | Tenancy & access | `orgs-and-teams`, `rbac-permissions` (existing draft), `invitations-and-seats`, `org-management-ui` |
35
- | C | Communications (pluggable) | `comms-abstraction`, one item **per adapter**: `comms-adapter-expo-push` (first push adapter, per D4), `comms-adapter-twilio-sms`, `comms-adapter-twilio-verify`, `comms-adapter-sendgrid` (first mail adapter, per D2); consumers: `password-reset-and-email-verification`, `notification-center` |
35
+ | C | Communications (pluggable) | `comms-abstraction`, one item **per adapter**: `comms-adapter-expo-push` (first push adapter, per D4), `comms-adapter-twilio-sms`, `comms-adapter-twilio-verify`, `comms-adapter-sendgrid` (first mail adapter, per D2); operations: `comms-admin-dashboard`; consumers: `password-reset-and-email-verification`, `notification-center` |
36
36
  | B | Billing | `inbound-webhooks`, `billing-stripe` (web-first, per D1), `mobile-iap-revenuecat` (Future) |
37
37
  | D | Zero-to-deployed DX | `create-terreno-app`, `mongo-migrations` (deploy items already exist in the OSS launch program) |
38
38
  | E | B2B UI surfaces | `data-grid-server-filters`, `charts-and-dashboards`, `dark-mode-theme`, `command-palette`, `wizard-stepper`, `wysiwyg-editor` (markdown stays for now, per D3), `global-search` |
@@ -97,6 +97,7 @@ All decisions resolved by the maintainer on 2026-08-09:
97
97
  | [comms-adapter-twilio-sms](comms-adapter-twilio-sms.md) | Draft | [tasks](../tasks/comms-adapter-twilio-sms.md) |
98
98
  | [comms-adapter-twilio-verify](comms-adapter-twilio-verify.md) | Draft | [tasks](../tasks/comms-adapter-twilio-verify.md) |
99
99
  | [comms-adapter-sendgrid](comms-adapter-sendgrid.md) | Draft | [tasks](../tasks/comms-adapter-sendgrid.md) |
100
+ | [comms-admin-dashboard](comms-admin-dashboard.md) | Draft | [tasks](../tasks/comms-admin-dashboard.md) |
100
101
  | [password-reset-and-email-verification](password-reset-and-email-verification.md) | Draft | [tasks](../tasks/password-reset-and-email-verification.md) |
101
102
  | [orgs-and-teams](orgs-and-teams.md) | Draft | [tasks](../tasks/orgs-and-teams.md) |
102
103
  | [billing-stripe](billing-stripe.md) | Draft | [tasks](../tasks/billing-stripe.md) |