@terreno/mcp 57.21.0 → 58.0.0-beta.1

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 (244) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +57 -3
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +17 -16
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  7. package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
  8. package/dist/docs/prompts/debug.md +6 -4
  9. package/dist/docs/prompts/screen.md +11 -17
  10. package/dist/docs/resources/overview.md +5 -3
  11. package/dist/docs/resources/patterns.md +157 -238
  12. package/dist/docs/resources/syncdb.md +44 -16
  13. package/dist/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  14. package/dist/docs/templates/bootstrap/cursorrules.template.md +7 -5
  15. package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  16. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  17. package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  18. package/dist/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  19. package/dist/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  20. package/dist/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  21. package/dist/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  22. package/dist/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  23. package/dist/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  24. package/dist/docs/upgrades/58.0.0-beta.1.md +504 -0
  25. package/dist/docs/upgrades/58.0.0.md +495 -4
  26. package/dist/docs/versioned/next/README.md +2 -2
  27. package/dist/docs/versioned/next/approval-rubric.md +2 -2
  28. package/dist/docs/versioned/next/explanation/admin-interface.md +6 -5
  29. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  30. package/dist/docs/versioned/next/explanation/ai-workflows.md +5 -3
  31. package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  32. package/dist/docs/versioned/next/explanation/authentication.md +62 -43
  33. package/dist/docs/versioned/next/explanation/dependency-management.md +1 -1
  34. package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  35. package/dist/docs/versioned/next/explanation/example-coverage.md +2 -2
  36. package/dist/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  37. package/dist/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  38. package/dist/docs/versioned/next/explanation/local-first-data.md +11 -9
  39. package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
  40. package/dist/docs/versioned/next/explanation/modular-api-design.md +2 -2
  41. package/dist/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  42. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  43. package/dist/docs/versioned/next/explanation/source-rules.md +2 -2
  44. package/dist/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  45. package/dist/docs/versioned/next/explanation/versioning-policy.md +3 -3
  46. package/dist/docs/versioned/next/how-to/README.md +3 -2
  47. package/dist/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  48. package/dist/docs/versioned/next/how-to/add-organizations.md +2 -1
  49. package/dist/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  50. package/dist/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  51. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  52. package/dist/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  53. package/dist/docs/versioned/next/how-to/build-for-web.md +1 -1
  54. package/dist/docs/versioned/next/how-to/call-external-apis.md +2 -2
  55. package/dist/docs/versioned/next/how-to/circleci.md +12 -6
  56. package/dist/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  57. package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  58. package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  59. package/dist/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  60. package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  61. package/dist/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  62. package/dist/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  63. package/dist/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  64. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  65. package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
  66. package/dist/docs/versioned/next/how-to/product-announcements.md +6 -3
  67. package/dist/docs/versioned/next/how-to/rate-limiting.md +38 -16
  68. package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  69. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  70. package/dist/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  71. package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  72. package/dist/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  73. package/dist/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  74. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  75. package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  76. package/dist/docs/versioned/next/how-to/websocket-integration.md +113 -402
  77. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  78. package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  79. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  80. package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  81. package/dist/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  82. package/dist/docs/versioned/next/reference/README.md +1 -1
  83. package/dist/docs/versioned/next/reference/admin-frontend.md +85 -70
  84. package/dist/docs/versioned/next/reference/admin-spa.md +10 -4
  85. package/dist/docs/versioned/next/reference/ai.md +36 -4
  86. package/dist/docs/versioned/next/reference/announcements.md +3 -3
  87. package/dist/docs/versioned/next/reference/api.md +39 -24
  88. package/dist/docs/versioned/next/reference/cli.md +3 -4
  89. package/dist/docs/versioned/next/reference/environment-variables.md +16 -16
  90. package/dist/docs/versioned/next/reference/feature-flags.md +48 -7
  91. package/dist/docs/versioned/next/reference/jobs.md +2 -2
  92. package/dist/docs/versioned/next/reference/legacy/rtk.md +25 -600
  93. package/dist/docs/versioned/next/reference/mcp-server.md +441 -1158
  94. package/dist/docs/versioned/next/reference/syncdb.md +426 -16
  95. package/dist/docs/versioned/next/reference/test.md +28 -3
  96. package/dist/docs/versioned/next/reference/ui.md +132 -19
  97. package/dist/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  98. package/dist/docs/versioned/next/tasks/rate-limiting.md +9 -2
  99. package/dist/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  100. package/dist/docs/versioned/next/tasks/remove-rtk.md +155 -0
  101. package/dist/local/localTools.d.ts.map +1 -1
  102. package/dist/local/localTools.js +3 -54
  103. package/dist/local/localTools.js.map +1 -1
  104. package/dist/local/tools/runtime.d.ts +0 -21
  105. package/dist/local/tools/runtime.d.ts.map +1 -1
  106. package/dist/local/tools/runtime.js +0 -176
  107. package/dist/local/tools/runtime.js.map +1 -1
  108. package/dist/local/tools/syncDb.d.ts +0 -11
  109. package/dist/local/tools/syncDb.d.ts.map +1 -1
  110. package/dist/local/tools/syncDb.js +1 -5
  111. package/dist/local/tools/syncDb.js.map +1 -1
  112. package/dist/packageGuidelines.d.ts +3 -2
  113. package/dist/packageGuidelines.d.ts.map +1 -1
  114. package/dist/packageGuidelines.js +4 -11
  115. package/dist/packageGuidelines.js.map +1 -1
  116. package/dist/prompts.d.ts.map +1 -1
  117. package/dist/prompts.js +17 -18
  118. package/dist/prompts.js.map +1 -1
  119. package/dist/resources.d.ts.map +1 -1
  120. package/dist/resources.js +1 -8
  121. package/dist/resources.js.map +1 -1
  122. package/dist/search/chunker.d.ts +1 -1
  123. package/dist/search/chunker.d.ts.map +1 -1
  124. package/dist/search/inferPackages.d.ts +1 -1
  125. package/dist/search/inferPackages.js +5 -5
  126. package/dist/search/inferPackages.js.map +1 -1
  127. package/dist/tools.d.ts +6 -0
  128. package/dist/tools.d.ts.map +1 -1
  129. package/dist/tools.js +126 -119
  130. package/dist/tools.js.map +1 -1
  131. package/package.json +6 -6
  132. package/src/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  133. package/src/docs/guidelines/ui/guidelines/core.md +1 -1
  134. package/src/docs/prompts/debug.md +6 -4
  135. package/src/docs/prompts/screen.md +11 -17
  136. package/src/docs/resources/overview.md +5 -3
  137. package/src/docs/resources/patterns.md +157 -238
  138. package/src/docs/resources/syncdb.md +44 -16
  139. package/src/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  140. package/src/docs/templates/bootstrap/cursorrules.template.md +7 -5
  141. package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  142. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  143. package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  144. package/src/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  145. package/src/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  146. package/src/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  147. package/src/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  148. package/src/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  149. package/src/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  150. package/src/docs/upgrades/58.0.0-beta.1.md +504 -0
  151. package/src/docs/upgrades/58.0.0.md +495 -4
  152. package/src/docs/versioned/next/README.md +2 -2
  153. package/src/docs/versioned/next/approval-rubric.md +2 -2
  154. package/src/docs/versioned/next/explanation/admin-interface.md +6 -5
  155. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  156. package/src/docs/versioned/next/explanation/ai-workflows.md +5 -3
  157. package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  158. package/src/docs/versioned/next/explanation/authentication.md +62 -43
  159. package/src/docs/versioned/next/explanation/dependency-management.md +1 -1
  160. package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  161. package/src/docs/versioned/next/explanation/example-coverage.md +2 -2
  162. package/src/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  163. package/src/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  164. package/src/docs/versioned/next/explanation/local-first-data.md +11 -9
  165. package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
  166. package/src/docs/versioned/next/explanation/modular-api-design.md +2 -2
  167. package/src/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  168. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  169. package/src/docs/versioned/next/explanation/source-rules.md +2 -2
  170. package/src/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  171. package/src/docs/versioned/next/explanation/versioning-policy.md +3 -3
  172. package/src/docs/versioned/next/how-to/README.md +3 -2
  173. package/src/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  174. package/src/docs/versioned/next/how-to/add-organizations.md +2 -1
  175. package/src/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  176. package/src/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  177. package/src/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  178. package/src/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  179. package/src/docs/versioned/next/how-to/build-for-web.md +1 -1
  180. package/src/docs/versioned/next/how-to/call-external-apis.md +2 -2
  181. package/src/docs/versioned/next/how-to/circleci.md +12 -6
  182. package/src/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  183. package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  184. package/src/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  185. package/src/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  186. package/src/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  187. package/src/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  188. package/src/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  189. package/src/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  190. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  191. package/src/docs/versioned/next/how-to/password-reset.md +1 -1
  192. package/src/docs/versioned/next/how-to/product-announcements.md +6 -3
  193. package/src/docs/versioned/next/how-to/rate-limiting.md +38 -16
  194. package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  195. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  196. package/src/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  197. package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  198. package/src/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  199. package/src/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  200. package/src/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  201. package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  202. package/src/docs/versioned/next/how-to/websocket-integration.md +113 -402
  203. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  204. package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  205. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  206. package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  207. package/src/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  208. package/src/docs/versioned/next/reference/README.md +1 -1
  209. package/src/docs/versioned/next/reference/admin-frontend.md +85 -70
  210. package/src/docs/versioned/next/reference/admin-spa.md +10 -4
  211. package/src/docs/versioned/next/reference/ai.md +36 -4
  212. package/src/docs/versioned/next/reference/announcements.md +3 -3
  213. package/src/docs/versioned/next/reference/api.md +39 -24
  214. package/src/docs/versioned/next/reference/cli.md +3 -4
  215. package/src/docs/versioned/next/reference/environment-variables.md +16 -16
  216. package/src/docs/versioned/next/reference/feature-flags.md +48 -7
  217. package/src/docs/versioned/next/reference/jobs.md +2 -2
  218. package/src/docs/versioned/next/reference/legacy/rtk.md +25 -600
  219. package/src/docs/versioned/next/reference/mcp-server.md +441 -1158
  220. package/src/docs/versioned/next/reference/syncdb.md +426 -16
  221. package/src/docs/versioned/next/reference/test.md +28 -3
  222. package/src/docs/versioned/next/reference/ui.md +132 -19
  223. package/src/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  224. package/src/docs/versioned/next/tasks/rate-limiting.md +9 -2
  225. package/src/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  226. package/src/docs/versioned/next/tasks/remove-rtk.md +155 -0
  227. package/dist/docs/guidelines/rtk/guidelines/core.md +0 -26
  228. package/dist/docs/resources/rtk.md +0 -181
  229. package/dist/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  230. package/dist/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  231. package/dist/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  232. package/dist/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  233. package/dist/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  234. package/dist/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  235. package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
  236. package/src/docs/guidelines/rtk/guidelines/core.md +0 -26
  237. package/src/docs/resources/rtk.md +0 -181
  238. package/src/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  239. package/src/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  240. package/src/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  241. package/src/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  242. package/src/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  243. package/src/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  244. package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
@@ -597,9 +597,9 @@ pickers, sort menus).
597
597
  </DropdownPanel>
598
598
  ``````
599
599
 
600
- > `Filter` is the former name and stays exported as a deprecated alias (with `FilterProps`)
601
- > until Terreno 58. The `FilterSelectMenu` / `FilterBoolean` / `FilterAccordion` /
602
- > `FilterChangesBadge` controls keep their names.
600
+ > `Filter` and `FilterProps`, the former names, were removed in Terreno 58. The
601
+ > `FilterSelectMenu` / `FilterBoolean` / `FilterAccordion` / `FilterChangesBadge` controls
602
+ > keep their names.
603
603
 
604
604
  #### Positioning
605
605
 
@@ -1251,18 +1251,87 @@ On web, that container is portaled to `document.body` (`position: fixed`, `z-ind
1251
1251
 
1252
1252
  Only the visible toast takes clicks. Every full-width layer around it (the container, its safe area, and the `Toast` layout wrapper) uses `pointerEvents: "box-none"`, so a button at the same height as an open toast stays clickable. Declare that style through `StyleSheet.create`; react-native-web drops `pointerEvents` from inline styles.
1253
1253
 
1254
- ## Consent components
1254
+ ## Consent and announcement navigators
1255
1255
 
1256
- Client side of `ConsentApp` ([API reference](api.md#consent-forms-consentapp),
1257
- [concepts](../explanation/consent-forms.md)). Lazily exported from the package root.
1256
+ `ConsentNavigator` and `AnnouncementNavigator` gate authenticated app content on the
1257
+ consent (`@terreno/api` consents) and announcement (`@terreno/announcements`) routes.
1258
+ Both take a request `client` instead of an RTK Query api (the `api` prop was removed in
1259
+ Terreno 58). Pass the app's `@terreno/syncdb/api` client:
1258
1260
 
1259
- ### ConsentNavigator and ConsentFormScreen
1261
+ ```tsx
1262
+ import {AnnouncementNavigator, ConsentNavigator} from "@terreno/ui";
1263
+ import {apiClient} from "@/store/apiClient"; // createApiClient({authProvider, baseUrl, ...})
1264
+
1265
+ <ConsentNavigator client={apiClient}>
1266
+ <AnnouncementNavigator client={apiClient} frequency={{userId}}>
1267
+ <AppTabs />
1268
+ </AnnouncementNavigator>
1269
+ </ConsentNavigator>
1270
+ ```
1271
+
1272
+ ### `TerrenoRequestClient`
1273
+
1274
+ `client` is any object with a `request` method of this shape. A syncdb `ApiClient` satisfies
1275
+ it structurally; `@terreno/ui` does not depend on `@terreno/syncdb`.
1276
+
1277
+ ```typescript
1278
+ interface TerrenoRequestArgs {
1279
+ url: string; // relative paths resolve against the client's base URL
1280
+ method?: string; // default "GET"
1281
+ params?: Record<string, unknown>; // query string; undefined values are omitted
1282
+ body?: Record<string, unknown> | null;
1283
+ headers?: Record<string, string>;
1284
+ unwrap?: boolean; // unwrap {data} envelopes (default true)
1285
+ }
1286
+
1287
+ interface TerrenoRequestClient {
1288
+ request: <T = unknown>(args: TerrenoRequestArgs) => Promise<T>;
1289
+ }
1290
+ ```
1291
+
1292
+ A failed request must reject with an error carrying the HTTP `status` (syncdb's `ApiError`
1293
+ does). On `401` / `403` both navigators render `children` so the app's auth handling runs;
1294
+ any other error shows a **Retry** screen and calls `onError`.
1295
+
1296
+ ### `ConsentNavigator` props
1297
+
1298
+ | Prop | Type | Description |
1299
+ |------|------|-------------|
1300
+ | `client` | `TerrenoRequestClient` | Required. Sends `GET /consents/pending` and `POST /consents/respond`. |
1301
+ | `baseUrl` | `string` | Optional path prefix for the consent routes. |
1302
+ | `children` | `ReactNode` | Rendered once no consent forms are pending. |
1303
+ | `extraScreens` | `ReactNode[]` | Shown in order after the forms; each element receives an `onNext` prop. |
1304
+ | `onError` | `(error: unknown) => void` | Called for non-auth load errors and failed submissions. |
1305
+ | `variables` | `Record<string, string>` | Substituted into consent form markdown. |
1306
+
1307
+ ### `AnnouncementNavigator` props
1308
+
1309
+ | Prop | Type | Description |
1310
+ |------|------|-------------|
1311
+ | `client` | `TerrenoRequestClient` | Required. Sends `GET /announcements/pending`, `GET /announcements/feed`, and the acknowledge / impression / click events. |
1312
+ | `baseUrl` | `string` | Optional path prefix for the announcement routes. |
1313
+ | `children` | `ReactNode` | App content; hidden behind a modal announcement, kept mounted under a banner. |
1314
+ | `frequency` | `AnnouncementFrequencyConfig` | Client-side interrupt caps (`maxInterruptionsPerSession`, `cooldownHours`, `skipFirstLaunch`, `userId`). |
1315
+ | `onError` | `(error: unknown) => void` | Called for non-auth load errors and failed acknowledgements. |
1316
+
1317
+ ### Hooks
1260
1318
 
1261
- `ConsentNavigator` wraps app content, fetches `GET /consents/pending` through
1262
- `useConsentForms(api, baseUrl)`, and renders `ConsentFormScreen` for each pending form until
1263
- the list is empty. `ConsentFormScreen` shows the form's markdown (with `{{variable}}`
1264
- substitution), required checkboxes, an optional signature, the scroll-to-bottom gate, and the
1265
- agree and decline buttons.
1319
+ The navigators are built on hooks you can call directly. Each takes `(client, baseUrl?)` and
1320
+ keeps loading, error, and data in React state.
1321
+
1322
+ | Hook | Returns |
1323
+ |------|---------|
1324
+ | `useConsentForms(client, baseUrl?)` | `{forms, isLoading, error, refetch}` |
1325
+ | `useSubmitConsent(client, baseUrl?)` | `{submit, isSubmitting, error}` |
1326
+ | `useAnnouncements(client, baseUrl?, {page, limit}?)` | `{pending, feed, isLoading, error, refetch, isFeedLoading, feedError, refetchFeed}` |
1327
+ | `useAcknowledgeAnnouncement(client, baseUrl?)` | `{acknowledge, recordImpression, recordClick, isSubmitting, error, ...}` |
1328
+
1329
+ Queries fetch on mount and when `client`, `baseUrl`, or feed paging change. `refetch()` joins
1330
+ a request already in flight. A successful `submit` refetches every mounted `useConsentForms`
1331
+ on the same client, and a successful `acknowledge` refetches every mounted pending
1332
+ announcement on the same client.
1333
+
1334
+ ### Provisional signatures on consent forms
1266
1335
 
1267
1336
  When staff signed the form provisionally, the pending form carries
1268
1337
  `provisionalSignature: {signedAt, signedByName}` (`ConsentFormPublic`), and the screen shows
@@ -1284,12 +1353,12 @@ For staff who record consent on a user's behalf. The server must grant the viewe
1284
1353
  ```tsx
1285
1354
  import {ProvisionalConsentSigner} from "@terreno/ui";
1286
1355
 
1287
- <ProvisionalConsentSigner api={terrenoApi} userId={memberId} />
1356
+ <ProvisionalConsentSigner client={apiClient} userId={memberId} />
1288
1357
  ```
1289
1358
 
1290
1359
  | Prop | Type | Description |
1291
1360
  | --- | --- | --- |
1292
- | `api` | RTK Query api | The consumer api, as passed to `ConsentNavigator` |
1361
+ | `client` | `TerrenoRequestClient` | The app's request client, as passed to `ConsentNavigator` |
1293
1362
  | `baseUrl` | `string` | Optional prefix for consent routes |
1294
1363
  | `userId` | `string` | The user being signed for |
1295
1364
  | `onSigned` | `(response) => void` | Called with the stored `ConsentResponse` after a provisional signature |
@@ -1318,14 +1387,14 @@ the signer, date, and note. Provisional rows also have "Void". It opens a dialog
1318
1387
  a required reason, sends it to `POST /consent-responses/:id/void`, and refetches, so the row
1319
1388
  returns to "Pending". A void error shows in the dialog and calls `onError`.
1320
1389
 
1321
- `useProvisionalConsent({api, baseUrl?, userId})` returns `{statuses, isLoading, error,
1390
+ `useProvisionalConsent({client, baseUrl?, userId})` returns `{statuses, isLoading, error,
1322
1391
  refetch, sign, isSigning, voidSignature, isVoiding}`. `statuses` are `ProvisionalConsentStatusRow`s
1323
1392
  (`{form, status, isConsented, respondedAt?, provisional?}`). `sign({consentFormId, note,
1324
1393
  locale?})` signs for the hook's `userId` and resolves to the `ConsentResponse`, or rejects
1325
- with the RTK error. `voidSignature({responseId, reason})` voids a provisional signature the
1326
- same way. The hook injects `getConsentStatus`, `signConsentProvisionally`, and
1327
- `voidProvisionalConsent` once per `(api, baseUrl)`. Signing and voiding invalidate the
1328
- `ConsentStatus` and `PendingConsents` tags.
1394
+ with the request error. `voidSignature({responseId, reason})` voids a provisional signature the
1395
+ same way. The hook requests `GET /consent-responses/status`, `POST /consent-responses/provisional`,
1396
+ and `POST /consent-responses/:id/void` on the client. Signing and voiding refetch every mounted
1397
+ `useProvisionalConsent` and `useConsentForms` on the same client.
1329
1398
 
1330
1399
  testIDs: `provisional-consent-signer`, `provisional-consent-loading`,
1331
1400
  `provisional-consent-error`, `provisional-consent-retry-button`, `provisional-consent-empty`,
@@ -2391,6 +2460,21 @@ describe("MyComponent", () => {
2391
2460
 
2392
2461
  **Why:** Most @terreno/ui components require ThemeProvider context to access theme values.
2393
2462
 
2463
+ ### createFakeRequestClient
2464
+
2465
+ In-repo test helper (`ui/src/test-utils.tsx`) for components that take a
2466
+ `TerrenoRequestClient`. Routes are `"METHOD /path/:param"` keys; unmatched requests reject
2467
+ with a 404-shaped error. `fakeHttpError(status)` builds an error carrying `status`.
2468
+
2469
+ ``````typescript
2470
+ const {client, callsTo} = createFakeRequestClient({
2471
+ "GET /consents/pending": () => ({data: [form]}),
2472
+ "POST /consents/respond": () => ({agreed: true}),
2473
+ });
2474
+ renderWithTheme(<ConsentNavigator client={client}>{children}</ConsentNavigator>);
2475
+ expect(callsTo("POST /consents/respond")).toHaveLength(1);
2476
+ ``````
2477
+
2394
2478
  ### TapToEdit testIDs
2395
2479
 
2396
2480
  Pass `testID` on `TapToEdit`. The Field input uses that id. Action controls suffix it:
@@ -2823,6 +2907,35 @@ for that type:
2823
2907
  A value that is already formatted, such as "Oct 8, 2026, 3:00 PM", is not ISO, so a `date`
2824
2908
  column shows it unchanged.
2825
2909
 
2910
+ ## DataTable column widths
2911
+
2912
+ `width` on a `DataTableColumn` is optional. A column with a `width` is always that many
2913
+ pixels. A column without one is sized automatically:
2914
+
2915
+ 1. Its natural width is the wider of its header (title plus the sort, info, and filter icons)
2916
+ and its content, plus cell padding, clamped to 80–360px. Content is read from at most 50
2917
+ rows, spread evenly across `data`. Dates are measured as formatted; boolean cells are a
2918
+ fixed icon width; custom cells whose value is not a string or number count as 120px.
2919
+ 2. When the table body is wider than the sum of all columns, auto-sized columns grow to fill
2920
+ it, in proportion to their natural widths. When the columns are wider than the body, they
2921
+ keep their natural widths and the table scrolls horizontally.
2922
+
2923
+ Text is measured with one shared canvas on web and estimated from font size on native, so
2924
+ sizing never renders or lays out extra cells. Measurement reruns only when `columns`, `data`,
2925
+ `defaultTextSize`, or `customColumnComponentMap` change; a container resize only reruns the
2926
+ fill step. Keep `columns` and `data` referentially stable between renders.
2927
+
2928
+ ```typescript
2929
+ const columns: DataTableColumn[] = [
2930
+ {columnType: "text", title: "Name"}, // auto
2931
+ {columnType: "text", title: "Email"}, // auto
2932
+ {columnType: "number", title: "Visits"}, // auto
2933
+ {columnType: "custom", title: "", width: 60}, // fixed
2934
+ ];
2935
+ ```
2936
+
2937
+ Custom cell components receive the column with its resolved `width`.
2938
+
2826
2939
  ## DataTable server-side filtering
2827
2940
 
2828
2941
  `DataTable` is data-layer agnostic. Optional filter and search props emit a
@@ -4,16 +4,18 @@ IP: [native-module-baseline](../implementationPlans/native-module-baseline.md)
4
4
 
5
5
  ## Phase 1 — Dependencies + config
6
6
 
7
- - [ ] **Task 1.1**: Catalog + app package manifests
7
+ - [x] **Task 1.1**: Catalog + app package manifests
8
8
  - Description: add the seven packages to root catalog (sorted) and to example-frontend + demo deps via `catalog:`
9
9
  - Files: root `package.json`, `example-frontend/package.json`, `demo/package.json`
10
10
  - Depends on: none
11
11
  - Acceptance: `bun install` + full compile green
12
- - [ ] **Task 1.2**: Config plugins + build properties
12
+ - Done: six of seven manifest entries landed; `react-native-otp-verify` dropped (no New Architecture support). See IP Implementation notes.
13
+ - [x] **Task 1.2**: Config plugins + build properties
13
14
  - Description: stripe + purchases plugins; `expo-build-properties` Kotlin 2.x / compileSdk 36 pins
14
15
  - Files: `example-frontend/app.json`, `demo/app.json`
15
16
  - Depends on: 1.1
16
17
  - Acceptance: `expo config` resolves without errors; web export unaffected
18
+ - Done: stripe + local-authentication + build-properties plugins. `react-native-purchases` ships no config plugin. compileSdk pinned to 37 (RN 0.88 default), not 36.
17
19
 
18
20
  ## Phase 2 — Builds + verification
19
21
 
@@ -22,16 +24,18 @@ IP: [native-module-baseline](../implementationPlans/native-module-baseline.md)
22
24
  - Files: `eas.json` profiles if needed
23
25
  - Depends on: Phase 1
24
26
  - Acceptance: both builds compile
25
- - [ ] **Task 2.2**: Diagnostics story
27
+ - [x] **Task 2.2**: Diagnostics story
26
28
  - Description: hidden demo story listing resolved native module versions
27
29
  - Files: `demo/stories/NativeModules.stories.tsx`, `demo/demoConfig.tsx`
28
30
  - Depends on: 2.1
29
31
  - Acceptance: story shows all seven modules on a dev build; screenshot evidence
30
- - [ ] **Task 2.3**: Expo Go degradation doc
32
+ - Done: hidden `Native Modules` page (`hidden: true` config, routable at `/demo/Native%20Modules`). Web screenshot only; on-device check waits on Task 2.1.
33
+ - [x] **Task 2.3**: Expo Go degradation doc
31
34
  - Description: document which flows need a dev build (stripe, local-auth) vs preview modes (purchases)
32
35
  - Files: `docs/` upgrade note draft
33
36
  - Depends on: 2.1
34
37
  - Acceptance: reviewed checklist in the note
38
+ - Done: Expo Go table in `mcp-server/src/docs/upgrades/58.0.0.md`.
35
39
 
36
40
  ## Phase 3 — Release
37
41
 
@@ -8,12 +8,12 @@ IP: [rate-limiting.md](../implementationPlans/rate-limiting.md)
8
8
  ## Phase 1 — Tracer (memory, TerrenoApp)
9
9
 
10
10
  - [x] **Task 1.1**: Rate-limit module + TerrenoApp mount
11
- - Delivers: `rateLimit: {}` on `TerrenoApp` limits HTTP; omitted option is a no-op; `POST /auth/login` uses the auth bucket (20 / 15 min); modelRouter list uses `api` (600 / 15 min); skip `/health` and `/openapi.json`; 429 `APIError` `code: "rate-limit-exceeded"` with `Retry-After` and IETF `RateLimit` / `RateLimit-Policy`; memory store; `trust proxy` default `1` when enabled
11
+ - Delivers: `rateLimit: {}` on `TerrenoApp` limits HTTP; omitted option used to be a no-op (Terreno 58 default-on — see Task 5.1); `POST /auth/login` uses the auth bucket (20 / 15 min); modelRouter list uses `api` (600 / 15 min); skip `/health` and `/openapi.json`; 429 `APIError` `code: "rate-limit-exceeded"` with `Retry-After` and IETF `RateLimit` / `RateLimit-Policy`; memory store; `trust proxy` default `1` when enabled
12
12
  - Files: `api/src/rateLimit/types.ts`, `api/src/rateLimit/store.ts`, `api/src/rateLimit/memoryStore.ts`, `api/src/rateLimit/policies.ts`, `api/src/rateLimit/middleware.ts`, `api/src/rateLimit/rateLimit.test.ts`, `api/src/terrenoApp.ts`, `api/src/index.ts`, `api/src/api.ts` (remove `TODO add rate limit`)
13
13
  - Blocked by: none
14
14
  - Skills: `terreno-backend-api`, `backend-test-env`, `update-docs`
15
15
  - Docs: stub section in `docs/reference/api.md` (`rateLimit` option) so the tracer is documented when it lands
16
- - Acceptance: bun tests — default off (burst login no 429); auth 20 then 429; 21st list GET still 200; 601st list 429; `/health` never 429; 429 body/headers; login classified auth
16
+ - Acceptance: bun tests — with limiter off (`rateLimit: false`), burst login no 429; auth 20 then 429; 21st list GET still 200; 601st list 429; `/health` never 429; 429 body/headers; login classified auth
17
17
 
18
18
  ## Phase 2 — Shared stores
19
19
 
@@ -68,3 +68,10 @@ IP: [rate-limiting.md](../implementationPlans/rate-limiting.md)
68
68
  - Blocked by: 4.1
69
69
  - Skills: `update-docs`
70
70
  - Acceptance: seed `Implementation plan` / `Tasks` links are not `*(not yet written)*`
71
+
72
+ ## Phase 5 — Terreno 58
73
+
74
+ - [x] **Task 5.1**: Default the limiter on
75
+ - Delivers: omitted `rateLimit` mounts the memory limiter with default limits; `rateLimit: false` turns it off; `{}` / options unchanged; example-backend passes `{trustProxy: 1}` when deployed and `false` locally; 58 upgrade note
76
+ - Files: `api/src/terrenoApp.ts`, `api/src/rateLimit/rateLimit.test.ts`, `example-backend/src/server.ts`, `docs/how-to/rate-limiting.md`, `docs/reference/api.md`, `mcp-server/src/docs/upgrades/58.0.0.md`, `.rulesync/rules/api/00-api.md`
77
+ - Acceptance: omitted → 429 on the 21st login; `false` → 25 logins succeed; `{}` → 429 on the 21st login; backend suites stay green
@@ -28,31 +28,31 @@ See: [`docs/implementationPlans/remove-legacy-realtime.md`](../implementationPla
28
28
 
29
29
  ## Phase 2: Delete in Terreno 58
30
30
 
31
- - [ ] **Task 2.1**: Remove `modelRouter` `realtime` registration
31
+ - [x] **Task 2.1**: Remove `modelRouter` `realtime` registration
32
32
  - Delivers: passing `realtime` is a type error; no `registerRealtime` from `modelRouter`
33
33
  - Files: `api/src/api.ts`, `api/src/realtime/types.ts`, `api/src/realtime/deprecation.ts`, `api/src/index.ts`, `api/src/api.test.ts`
34
34
  - Blocked by: Terreno 58 major
35
35
  - Acceptance: TypeScript and tests fail if a router still passes `realtime`; `sync` registration is unchanged
36
36
 
37
- - [ ] **Task 2.2**: Stop emitting legacy `sync` (RTK) websocket events
37
+ - [x] **Task 2.2**: Stop emitting legacy `sync` (RTK) websocket events
38
38
  - Delivers: change streams emit `sync:delta` only for synced models; no RTK `sync` payload
39
39
  - Files: `api/src/realtime/changeStreamWatcher.ts`, `api/src/realtime/realtimeApp.ts`, `api/src/realtime/registry.ts`, related tests
40
40
  - Blocked by: Task 2.1
41
41
  - Acceptance: a model with only `sync` still emits `sync:delta`; no `sync` events for collection CRUD; `admin:model.changed` still works when `admin.realtime` is true
42
42
 
43
- - [ ] **Task 2.3**: Remove `@terreno/rtk` cache-patching helpers
43
+ - [x] **Task 2.3**: Remove `@terreno/rtk` cache-patching helpers
44
44
  - Delivers: `realtimeList`, `realtimeDocument`, `setRealtimeSocket`, `getRealtimeSocket` are gone from the package
45
45
  - Files: `rtk/src/realtime.ts`, `rtk/src/realtime.test.ts`, `rtk/src/index.ts`, `rtk/README.md`
46
46
  - Blocked by: Task 2.1
47
47
  - Acceptance: package compile fails if those symbols are imported; `useSocketConnection` still exists
48
48
 
49
- - [ ] **Task 2.4**: Remove example and bootstrap usage
49
+ - [x] **Task 2.4**: Remove example and bootstrap usage
50
50
  - Delivers: example-backend/example-frontend/MCP bootstrap do not configure RTK realtime
51
51
  - Files: `example-backend/src/api/todos.ts`, `example-frontend/store/sdk.ts`, `example-frontend/app/_layout.tsx`, MCP bootstrap templates
52
52
  - Blocked by: Task 2.3
53
53
  - Acceptance: todos stay on `sync` + syncdb; `setRealtimeSocket` / `realtimeList` / `realtimeDocument` are gone; `RealtimeApp` remains registered
54
54
 
55
- - [ ] **Task 2.5**: Docs, skills, changelog, 58.0.0 upgrade note
55
+ - [x] **Task 2.5**: Docs, skills, changelog, 58.0.0 upgrade note
56
56
  - Delivers: upgrade note lists every removed symbol and the syncdb replacement
57
57
  - Files: `docs/how-to/migrate-rtk-to-syncdb.md`, `docs/reference/api.md`, `.rulesync/skills/terreno-data-fetching/references/realtime.md`, `.rulesync/skills/terreno-backend-api/references/model-router.md`, `mcp-server/src/docs/upgrades/58.0.0.md`, changelog
58
58
  - Blocked by: Task 2.4
@@ -0,0 +1,155 @@
1
+ # Task List: Remove @terreno/rtk and Redux in Terreno 58
2
+
3
+ See: [`docs/implementationPlans/remove-rtk.md`](../implementationPlans/remove-rtk.md)
4
+
5
+ **Target:** Terreno **58.0.0**, branch `remove-rtk-58`, one PR into `release-58.0.0`.
6
+
7
+ Wide refactor: **expand → migrate → contract**. Every task leaves the branch compiling and
8
+ green. Phase 4.1 (delete `rtk/`) is blocked by every migration task.
9
+
10
+ **Supporting skills:** `terreno-data-fetching`, `terreno-backend-api`,
11
+ `mongoose-schema-safety`, `building-terreno-apps`, `building-admin-interfaces`, `terreno-ui`,
12
+ `backend-test-env`, `verify-ui-changes`, `update-docs`, `generate-sdk` (rewrite in 4.2).
13
+
14
+ ## Phase 1: Expand syncdb
15
+
16
+ - [x] **Task 1.1** (tracer): Request client + query/mutation hooks + `/config`
17
+ - Delivers: `createApiClient({baseUrl, authProvider, credentials?, headers?})` and `useApiQuery` / `useLazyApiQuery` / `useApiMutation` / `ApiClientProvider`, with a shared per-client cache keyed by URL+args, tag invalidation, `{data}` / list-envelope unwrap matching RTK, 401 → `authProvider.refresh` → retry, `App-Version` / `App-Platform` headers. `@terreno/syncdb/config` exports `baseUrl`, `baseWebsocketsUrl`, `resolveBaseUrls`, `resolveDevApiPort` (moved from `rtk/src/constants.ts` with their tests). example-frontend profile screen reads `GET /auth/me` through `useApiQuery`; `store/syncdb.ts` imports `baseUrl` from `@terreno/syncdb/config`.
18
+ - Files: `syncdb/src/api/{client,cache,hooks}.ts` + tests, `syncdb/src/config/baseUrl.ts` + tests, `syncdb/package.json` exports, `admin-frontend/src/{adminRequest,useAdminRpc}.ts` (wrap the syncdb client), `example-frontend/app/(tabs)/profile.tsx`, `example-frontend/store/syncdb.ts`
19
+ - Blocked by: none
20
+ - Acceptance: bun tests for dedupe, tag invalidation, envelope unwrap, refresh-retry, abort/timeout; admin-frontend tests still pass unchanged; profile e2e passes
21
+ - Docs: `docs/reference/syncdb.md` (API client, config sections)
22
+
23
+ - [x] **Task 1.2**: Codegen for every non-synced OpenAPI operation
24
+ - Delivers: `terreno-syncdb generate` also emits typed request/response types and `useXQuery` / `useXMutation` hooks for every operation without `x-terreno-sync`, including plain routes (`/auth/me`, `/consents/pending`, `/files/upload` multipart, `/version-check`). Tags inferred per path. example-frontend `bun run sdk` runs it and writes `store/apiSdk.ts`; profile screen switches to the generated `useGetAuthMeQuery`.
25
+ - Also: add OpenAPI docs for the 13 frontend-called routes the spec omits — `GET/PATCH /auth/me`, `POST /auth/{sendVerification,forgotPassword,verifyEmail}`, `POST /resetPassword` (`api`); `GET /feature-flags/flagConfiguration` (`feature-flags`); `GET /version-check` (`api`); `GET /consents/pending`, `POST /consents/respond` (`api`); `GET /announcements/{pending,feed}`, `POST /announcements/{id}/{acknowledge,click,impression}` (`announcements`); `POST /files/upload` multipart (`ai`).
26
+ - Files: `syncdb/src/codegen/{emitApi,discoverOperations}.ts` + fixtures/tests, `syncdb/src/codegen/{cli,generate}.ts`, `example-frontend/scripts/generate-sdk.ts`, `example-frontend/store/apiSdk.ts` (generated), OpenAPI registrations in the owning packages + their route tests
27
+ - Blocked by: 1.1
28
+ - Acceptance: codegen tests against fixture spec cover query, mutation, path params, multipart, synced ops excluded; generated file compiles in example-frontend; live `/openapi.json` lists all 13 previously missing routes
29
+ - Docs: `docs/reference/syncdb.md` (codegen), `docs/how-to/` page for regenerating the SDK
30
+
31
+ - [x] **Task 1.3**: `@terreno/syncdb/auth` — Better Auth client + session without Redux
32
+ - Delivers: `createBetterAuthClient`, `createStorageAdapter`, cookie-jar repair (moved with tests), `SessionProvider` + `useSession()` → `{userId, user, isLoading, isAuthenticated, error, logout}`; `logout` signs out, stops syncdb, wipes local data. example-frontend `lib/betterAuth.ts` imports from syncdb.
33
+ - Files: `syncdb/src/auth/{betterAuthClient,session,SessionProvider}.ts(x)` + tests, `syncdb/package.json` (move `better-auth`, `@better-auth/expo`, `expo-secure-store`, async-storage deps/peers), `example-frontend/lib/betterAuth.ts`
34
+ - Blocked by: 1.1
35
+ - Acceptance: ported `betterAuthClient` / `betterAuthSlice` behaviours as hook tests (session sync on start, after login, logout side effects)
36
+ - Docs: `docs/reference/syncdb.md` (auth), `docs/explanation/authentication.md`, `docs/how-to/configure-better-auth.md`
37
+
38
+ - [x] **Task 1.4**: Port legacy JWT frontend auth
39
+ - Delivers: `jwtAuthAdapter` (token storage on SecureStore/AsyncStorage, refresh when < 2 min to expiry, mutex, 401 → refresh → retry, logout on refresh failure) and `useJwtAuth()` (`login`, `signup`, `googleLogin`, `resetPassword`, `logout`) feeding `SessionProvider`; token-expiry helpers (`getTokenExpirationTimes`, `shouldShowStillThereModal`).
40
+ - Files: `syncdb/src/auth/{jwtAuthAdapter,useJwtAuth,tokenExpiry}.ts` + tests (ported from `rtk/src/authSlice*.test.ts`, `rtk/src/isolated/emptyApi*.isolated.ts`)
41
+ - Blocked by: 1.3
42
+ - Acceptance: AC9 — ported tests pass against an `api` test server using JWT auth
43
+ - Docs: `docs/explanation/authentication.md`, `docs/reference/syncdb.md`
44
+
45
+ - [x] **Task 1.5**: Socket event API + `@terreno/syncdb/flags`
46
+ - Delivers: `client.onSocketEvent` / `useSocketEvent`; `useFeatureFlags(client)` / `useTerrenoFeatureFlags` (OpenFeature provider) fetching via the request client and refetching on the flags push event. example-frontend `_layout.tsx` drops `useSocketConnection` / `useRealtimeDebug`. Server deprecation message updated.
47
+ - Files: `syncdb/src/sync/socketTransport.ts`, `syncdb/src/react/useSocketEvent.ts`, `syncdb/src/flags/*` + tests (ported from `rtk/src/useFeatureFlags.test.ts`, `useTerrenoFeatureFlags.test.ts`), `feature-flags/src/featureFlagsApp.ts`, `example-frontend/app/_layout.tsx`, `(tabs)/{ai,documents,profile}.tsx`
48
+ - Blocked by: 1.1
49
+ - Acceptance: AC7 — Playwright spec asserts one socket.io connection and a pushed flag change updates the UI
50
+ - Docs: `docs/reference/feature-flags.md`, `docs/how-to/add-feature-flags.md`, `docs/how-to/websocket-integration.md`
51
+
52
+ - [x] **Task 1.6**: `@terreno/syncdb/app` and `/buildNumber`
53
+ - Delivers: `useUpgradeCheck` (on the request client), `hasPermission` / `canOpenAdminPage`, `installDevConsoleLogger`, `useMCPTools` (token from the shared AuthProvider, fixing the Better Auth gap), `buildNumber` node subpath — all moved with tests. example-frontend uses them.
54
+ - Files: `syncdb/src/app/*`, `syncdb/src/buildNumber.ts` + tests, `example-frontend/app/_layout.tsx`, `app/admin/_layout.tsx`, `(tabs)/{ai,profile}.tsx`
55
+ - Blocked by: 1.1
56
+ - Acceptance: ported `useUpgradeCheck*`, `usePermissions`, `devConsoleLogger`, `useMCPTools`, `buildNumber` tests pass
57
+ - Docs: `docs/how-to/upgrade-banner.md`, `docs/how-to/expose-mcp-tools.md`, `docs/reference/syncdb.md`
58
+
59
+ ## Phase 2: Backend sync for GptHistory and McpServiceToken
60
+
61
+ - [x] **Task 2.1**: GptHistory synced with string ids + migration
62
+ - Delivers: String `_id` (UUID default), `syncPlugin` + `isDeletedPlugin`, `sync: {scope: owner}` on `/gpt/histories`; `AIDataset.historyId` String; `gpt-history-string-ids` migration; SSE / turn / actions routes accept string ids. Backend example registers the stream.
63
+ - Files: `ai/src/models/{gptHistory,aiDataset}.ts`, `ai/src/routes/{gptHistories,gpt,gptActions}.ts`, `ai/src/migrations/gptHistoryStringIds.ts` + tests, `example-backend/src/server.ts`
64
+ - Blocked by: none
65
+ - Acceptance: AC5; existing `ai` route tests pass with string ids; sync snapshot test returns only the owner's histories
66
+ - Docs: `docs/reference/ai.md`, `.rulesync/rules/ai/00-ai.md`, 58.0.0 upgrade notes (run migration first)
67
+ - Skills: `mongoose-schema-safety`
68
+
69
+ - [x] **Task 2.2**: McpServiceToken synced read-only + actions + migration
70
+ - Delivers: `modelRouter("/mcp/service-tokens", McpServiceToken, {sync})` with client writes disabled, sync `responseHandler` stripping `tokenHash`; `collectionActions.create` (plaintext once), `instanceActions.revoke`; `mcp-service-token-string-ids` migration; `verify` unchanged.
71
+ - Files: `api/src/models/mcpServiceToken.ts`, `api/src/mcp/serviceTokens.ts`, `api/src/migrations/` (built-in migration), tests
72
+ - Blocked by: none
73
+ - Acceptance: AC6; migration idempotence test; existing MCP auth tests pass
74
+ - Docs: `docs/reference/mcp-server.md` / service-token how-to, upgrade notes
75
+
76
+ ## Phase 3: Migrate consumers
77
+
78
+ - [x] **Task 3.1**: `@terreno/ui` consents and announcements take `client`
79
+ - Delivers: `TerrenoRequestClient` structural interface; `ConsentNavigator`, `AnnouncementNavigator`, `useConsentForms`, `useSubmitConsent`, `useAnnouncements`, `useAcknowledgeAnnouncement` take `client`; no `injectEndpoints`. Tests use a fake client.
80
+ - Files: `ui/src/requestClient.ts`, the six source files above and their tests, `ui/src/OfflineBanner.tsx` (JSDoc)
81
+ - Blocked by: 1.1
82
+ - Acceptance: ui suite green; `rg injectEndpoints ui/src` → none
83
+ - Docs: `docs/reference/ui.md` (consent / announcement props)
84
+
85
+ - [x] **Task 3.2**: example-frontend — remove Redux store, session, auth screens
86
+ - Delivers: delete `store/index.ts`, `store/appState.ts` Redux slice (→ context + `useStoredState`), `store/errors.ts` middleware (→ client `onError` reporting to Sentry), `store/betterAuthApi.ts`; root layout uses `SessionProvider` + `ApiClientProvider`; login, signup, forgot / reset password, verify email use generated hooks / `useSession`.
87
+ - Files: `example-frontend/store/*`, `app/_layout.tsx`, `app/{login,signup,forgotPassword,resetPassword,verifyEmail}.tsx`, `lib/authRecoveryActions.ts`, related tests
88
+ - Blocked by: 1.2, 1.3, 1.5, 1.6, 3.1
89
+ - Acceptance: auth e2e shard green; unit suite green
90
+ - Docs: `example-frontend/README.md`, `.rulesync/rules/example-frontend/00-example-frontend.md`
91
+
92
+ - [x] **Task 3.3**: example-frontend — profile, admin layout, notifications, documents, push tokens
93
+ - Delivers: all remaining non-AI screens on generated hooks; `store/sdk.ts`, `store/openApiSdk.ts`, `store/commsOpenApiSdk.ts`, `openapi-config.ts`, `comms-openapi-config.ts` deleted.
94
+ - Files: `app/(tabs)/{profile,documents}.tsx`, `app/admin/**`, `components/NotificationCenter.tsx`, `components/TodoSummaryCard.tsx`, `app/gcs-settings.tsx`, `hooks/useSyncLabEngines.ts`, `app/admin/SyncLabScreen.tsx`
95
+ - Blocked by: 3.2
96
+ - Acceptance: app + admin-core + admin-table e2e shards green
97
+ - Docs: none beyond 3.2 (record skip)
98
+
99
+ - [x] **Task 3.4**: example-frontend — AI screen and MCP settings on syncdb
100
+ - Delivers: `ai.tsx` histories via `useQuery("gptHistories")` / `useMutate` (rename, delete), no `updateQueryData` / `invalidateTags`; SSE uses the shared auth provider; MCP settings list via `useQuery("mcpServiceTokens")`, create/revoke via generated action hooks; `useMCPTools` from `/app`.
101
+ - Files: `app/(tabs)/ai.tsx`, `app/settings/mcp/index.tsx`, `store/syncdb.ts` (collections), `lib/gptAsks.ts`
102
+ - Blocked by: 2.1, 2.2, 3.2
103
+ - Acceptance: `ai-chat` e2e green incl. list / rename / delete across reload; new MCP tokens spec green
104
+ - Docs: `docs/how-to/` AI chat page if it names RTK hooks
105
+
106
+ - [x] **Task 3.5**: example-frontend tests off RTK
107
+ - Delivers: `tests/bunSetup.ts` / `testPreload.ts` no longer import or mock `@/store/sdk`; `documents.test.tsx`, `NotificationCenter.test.tsx`, `orgMembersRoute.test.tsx`, `errors.test.ts`, `gptAsks.test.ts`, `TodoSummaryCard.test.tsx` use the syncdb client / fake transport; e2e allowlists and comments drop RTK entries (`consoleAllowlist.ts`, `syncdbSuite.ts`, `offlineHelpers.ts`, `adminAuth.ts`, `login` / `signup` / `admin-todo-crud` specs).
108
+ - Files: listed above
109
+ - Blocked by: 3.3, 3.4
110
+ - Acceptance: AC3 and AC4 for example-frontend
111
+ - Docs: none (record skip)
112
+
113
+ - [x] **Task 3.6**: admin-spa off Redux
114
+ - Delivers: `StoreProvider` / `AdminGate` use `SessionProvider` (cookie mode); profile via the request client with `credentials: "same-origin"`; `store/sdk.ts` deleted; Redux deps removed.
115
+ - Files: `admin-spa/components/{StoreProvider,AdminGate,AdminSpaShell}.tsx`, `admin-spa/app/orgs/index.tsx`, `admin-spa/store/*`, `admin-spa/src/adminSpaUiCoverage.test.tsx`, `admin-spa/package.json`, `admin-spa/metro.config.js`
116
+ - Blocked by: 1.1, 1.3
117
+ - Acceptance: `bun run admin-spa:test` + admin-spa e2e and integration specs green
118
+ - Docs: `docs/reference/admin-spa*` / `docs/reference/admin-frontend.md` references
119
+
120
+ - [x] **Task 3.7**: admin-frontend RTK-shaped test doubles
121
+ - Delivers: observability / prompts isolated tests use fake request clients instead of `injectEndpoints` APIs; RTK wording in assertions removed.
122
+ - Files: `admin-frontend/src/isolated/{observabilityFakeApi,aiObservabilityScreens,AiPromptsListScreen,AiPromptEditorScreen,adminRpcHooks}.isolated.tsx`, the other test files named in the IP research
123
+ - Blocked by: 1.1
124
+ - Acceptance: admin-frontend suite green; `rg -i "rtk|injectEndpoints" admin-frontend/src` → none
125
+ - Docs: `.rulesync/rules/admin-frontend/00-admin-frontend.md` (layout example imports `baseUrl` from `/config`)
126
+
127
+ - [x] **Task 3.8**: Generators, MCP tools, cli
128
+ - Delivers: `create-terreno-app` scaffolds syncdb client, `SessionProvider`, generated `apiSdk.ts`, no Redux / rtk; `mcp-server` bootstrap templates, prompts, `tools.ts` (`generateInstallAdmin`), `resources.ts` (drop `terreno://docs/rtk`) updated; `get_rtk_state` and `terreno state --slice rtk` removed in favour of syncdb state tools.
129
+ - Files: `create-terreno-app/src/generate.ts` + tests, `mcp-server/src/{tools,prompts,bootstrap,resources}.ts`, `mcp-server/src/docs/templates/**`, `mcp-server/src/local/**`, `cli/src/commands/project.ts`, `cli/src/help.ts` + tests
130
+ - Blocked by: 1.2, 1.3, 1.5, 1.6
131
+ - Acceptance: AC10; generated app compiles in a temp dir
132
+ - Docs: `docs/how-to/create-a-terreno-app.md`, `docs/reference/mcp-server.md`, `docs/reference/cli.md`
133
+
134
+ ## Phase 4: Contract
135
+
136
+ - [x] **Task 4.1**: Delete `rtk/` and its wiring
137
+ - Delivers: `rtk/` removed; root workspaces, `rtk:*` scripts, catalog entries (`@reduxjs/toolkit`, `@rtk-query/codegen-openapi`, `redux-persist`, `react-redux`, `axios-retry`, `async-mutex`, `jwt-decode` if unused); metro aliases; `example-backend/Dockerfile`; `knip.jsonc`; `biome.jsonc`; `codecov.yml`; CircleCI (`config.yml`, `continue-config.yml`); GitHub workflows (`rtk-ci.yml` deleted; `publish-on-tag`, `cd`, `docs-deploy`, `eas-pr`, `frontend-example-deploy`, `admin-spa-ci`, `dependabot`, issue templates, `architectural-pr-review.ts`); `scripts/**` maps and their tests; `website/scripts` + docusaurus redirect; `mcp-server/scripts/sync-package-guidelines.ts`. Apps declare native deps rtk used to pull in (`expo-secure-store`, `@better-auth/expo`, `better-auth`, `socket.io-client`, `expo-network` if still used); Expo fingerprint change recorded.
138
+ - Files: listed above, `bun.lock`
139
+ - Blocked by: 3.1–3.8
140
+ - Acceptance: AC1, AC2; `bun install && bun run compile`; `bun run check:knip`; `bun run analyze:full`
141
+ - Docs: none beyond 4.2
142
+
143
+ - [x] **Task 4.2**: Docs, rules, skills, upgrade notes
144
+ - Delivers: `docs/how-to/migrate-rtk-to-syncdb.md` rewritten as the 58 upgrade path (every removed export → replacement table); `docs/reference/legacy/rtk.md` and `docs/explanation/announcements/rtk-deprecation.md` updated to "removed in 58"; `docs/explanation/local-first-data.md`, `docs/reference/environment-variables.md`, remaining single mentions; `mcp-server/src/docs/upgrades/58.0.0.md`; `changelog/unreleased/remove-rtk.md`; `.rulesync/rules/rtk/00-rtk.md` deleted, root / syncdb / example-frontend / mcp-server rules updated; plugin skills (`terreno-data-fetching`, `building-terreno-apps`, `generate-sdk`, `upgrading-terreno`, `building-admin-interfaces`); `README.md`, `AGENTS.md`, `CLAUDE.md`, `CLAUDE-consumer.md`, `CONTRIBUTING.md`; regenerate rulesync outputs and `bun run skills:sync`.
145
+ - Files: listed above
146
+ - Blocked by: 4.1
147
+ - Acceptance: AC12
148
+ - Skills: `update-docs`, `update-agent-docs`
149
+
150
+ - [x] **Task 4.3**: Full verification and UI evidence
151
+ - Delivers: `bun run prepush`; full `frontend:e2e`; admin-spa e2e; `maestro:test`; screenshots / videos for AC11 under `/opt/cursor/artifacts/`.
152
+ - Files: none (evidence only)
153
+ - Blocked by: 4.2
154
+ - Acceptance: AC3, AC4, AC11 with commands and artifact paths recorded in the PR Verification table
155
+ - Skills: `verify-ui-changes`
@@ -1 +1 @@
1
- {"version":3,"file":"localTools.d.ts","sourceRoot":"","sources":["../../src/local/localTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,8BAA8B,CAAC;AAGvD,OAAO,EAAkB,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAyC/D,eAAO,MAAM,aAAa,EAAE,IAAI,EAqP/B,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,gBAAe,OAAO,UAAuB,KAC5C,CAAC,CACF,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CAAC,CA4J3D,CAAC;AAEF,eAAO,MAAM,mBAAmB,SAhKxB,MAAM,QACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CA8JI,CAAC"}
1
+ {"version":3,"file":"localTools.d.ts","sourceRoot":"","sources":["../../src/local/localTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,8BAA8B,CAAC;AAGvD,OAAO,EAAkB,UAAU,EAAC,MAAM,oBAAoB,CAAC;AA4B/D,eAAO,MAAM,aAAa,EAAE,IAAI,EAuN/B,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,gBAAe,OAAO,UAAuB,KAC5C,CAAC,CACF,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CAAC,CA8I3D,CAAC;AAEF,eAAO,MAAM,mBAAmB,SAlJxB,MAAM,QACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,CAgJI,CAAC"}
@@ -3,9 +3,9 @@ import { isBrowserAction, useBrowser } from "./tools/browser.js";
3
3
  import { databaseQuery } from "./tools/databaseQuery.js";
4
4
  import { databaseSchema } from "./tools/databaseSchema.js";
5
5
  import { lastError, readLogs } from "./tools/readLogs.js";
6
- import { evaluate, getClientState, navigate } from "./tools/runtime.js";
6
+ import { evaluate, navigate } from "./tools/runtime.js";
7
7
  import { getSyncDbState, syncDbAction, syncDbSnapshot } from "./tools/syncDb.js";
8
- const CLIENT_STATE_INPUT_SCHEMA = {
8
+ const SYNCDB_STATE_INPUT_SCHEMA = {
9
9
  additionalProperties: false,
10
10
  properties: {
11
11
  collection: { description: "SyncDB: optional collection filter", type: "string" },
@@ -22,18 +22,9 @@ const CLIENT_STATE_INPUT_SCHEMA = {
22
22
  description: "SyncDB: client/store name; defaults to the first registered client",
23
23
  type: "string",
24
24
  },
25
- query: {
26
- description: "RTK: when slice is rtk/terreno-rtk (or omitted), filter cache rows whose endpoint name or serialized args contain this substring (case-insensitive).",
27
- type: "string",
28
- },
29
- slice: {
30
- description: "RTK: `auth`, `betterAuth`, `rtk`/`terreno-rtk`, or any top-level Redux state key. Omit for auth + RTK Query cache summary.",
31
- type: "string",
32
- },
33
25
  },
34
26
  type: "object",
35
27
  };
36
- const GET_CLIENT_STATE_DESCRIPTION = "Inspect the running app's client data layer. Detects the RTK Query Redux store (`registerTerrenoDevStore` / `globalThis.__TERRENO_STORE__`) and the SyncDB client (`createSyncDb({debug: true})` / `globalThis.__TERRENO_SYNCDB__`), in-process or via CDP. Returns JSON with `layer`: `rtk`, `syncdb`, `syncdb+rtk` (both present; both payloads returned), or `none`. `rtk` holds auth plus an RTK Query cache summary (queries/mutations with endpoint, status, args), narrowed by `slice`/`query`. `syncdb` holds the SyncDB inspect output (status, entities, outbox, conflicts, cursors, streams, debug events), narrowed by `name`/`collection`/`entityId`/`limit`/`includeDebugEvents`. `none` returns a setup `hint`, per-layer `reasons`, and the CDP `connection` status. Sensitive fields are redacted. Use `get_syncdb_state` for SyncDB-only reads.";
37
28
  export const localMcpTools = [
38
29
  {
39
30
  description: "Read the consumer app's package.json workspaces and key dependency versions (@terreno/*, Expo, React Native, Mongoose). Looks in `backend/` or `example-backend/`, and `frontend/` or `example-frontend/`. Call at the start of each chat and write version-specific code (Boost-style).",
@@ -103,38 +94,9 @@ export const localMcpTools = [
103
94
  },
104
95
  name: "last_error",
105
96
  },
106
- {
107
- description: GET_CLIENT_STATE_DESCRIPTION,
108
- inputSchema: CLIENT_STATE_INPUT_SCHEMA,
109
- name: "get_client_state",
110
- },
111
- {
112
- description: "Deprecated: use `get_client_state`. Alias kept for one release; it runs the same handler and returns the same `{layer, rtk?, syncdb?}` response.",
113
- inputSchema: CLIENT_STATE_INPUT_SCHEMA,
114
- name: "get_rtk_state",
115
- },
116
97
  {
117
98
  description: "Inspect a live SyncDB client registered by createSyncDb({debug: true}): status, entities (including tombstones/pending metadata), decoded outbox, conflicts, cursors, streams, repairs, values, and debugger events. Sensitive fields are redacted.",
118
- inputSchema: {
119
- additionalProperties: false,
120
- properties: {
121
- collection: { description: "Optional collection filter", type: "string" },
122
- entityId: { description: "Optional entity id filter", type: "string" },
123
- includeDebugEvents: {
124
- description: "Set false to return debug stats without event bodies",
125
- type: "boolean",
126
- },
127
- limit: {
128
- description: "Entity limit per collection (default 500, max 2000)",
129
- type: "number",
130
- },
131
- name: {
132
- description: "Client/store name; defaults to the first registered client",
133
- type: "string",
134
- },
135
- },
136
- type: "object",
137
- },
99
+ inputSchema: SYNCDB_STATE_INPUT_SCHEMA,
138
100
  name: "get_syncdb_state",
139
101
  },
140
102
  {
@@ -313,19 +275,6 @@ export const createLocalToolCallHandler = (browserRunner = useBrowser) => {
313
275
  });
314
276
  return { content: [{ text, type: "text" }] };
315
277
  }
316
- case "get_client_state":
317
- case "get_rtk_state": {
318
- const text = await getClientState({
319
- collection: typeof args.collection === "string" ? args.collection : undefined,
320
- entityId: typeof args.entityId === "string" ? args.entityId : undefined,
321
- includeDebugEvents: typeof args.includeDebugEvents === "boolean" ? args.includeDebugEvents : undefined,
322
- limit: typeof args.limit === "number" ? args.limit : undefined,
323
- name: typeof args.name === "string" ? args.name : undefined,
324
- query: typeof args.query === "string" ? args.query : undefined,
325
- slice: typeof args.slice === "string" ? args.slice : undefined,
326
- });
327
- return { content: [{ text, type: "text" }] };
328
- }
329
278
  case "get_syncdb_state": {
330
279
  const text = await getSyncDbState({
331
280
  collection: typeof args.collection === "string" ? args.collection : undefined,