@terreno/mcp 57.0.0 → 57.1.0

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 (220) hide show
  1. package/dist/bootstrap.d.ts +1 -1
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/bootstrap.js +381 -191
  4. package/dist/bootstrap.js.map +1 -1
  5. package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
  6. package/dist/docs/resources/overview.md +6 -1
  7. package/dist/docs/resources/rtk.md +3 -1
  8. package/dist/docs/resources/syncdb.md +43 -0
  9. package/dist/docs/upgrades/56.0.0-beta.2.md +34 -0
  10. package/dist/docs/upgrades/57.1.0.md +126 -0
  11. package/dist/docs/versioned/next/README.md +15 -3
  12. package/dist/docs/versioned/next/explanation/README.md +3 -0
  13. package/dist/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
  14. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
  15. package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
  16. package/dist/docs/versioned/next/explanation/authentication.md +51 -58
  17. package/dist/docs/versioned/next/explanation/local-first-data.md +52 -0
  18. package/dist/docs/versioned/next/explanation/loop-engineering.md +117 -0
  19. package/dist/docs/versioned/next/explanation/repository-settings.md +8 -0
  20. package/dist/docs/versioned/next/explanation/roadmap-process.md +22 -9
  21. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
  22. package/dist/docs/versioned/next/how-to/README.md +3 -0
  23. package/dist/docs/versioned/next/how-to/admin-add-model.md +41 -0
  24. package/dist/docs/versioned/next/how-to/admin-custom-home.md +31 -0
  25. package/dist/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
  26. package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
  27. package/dist/docs/versioned/next/how-to/circleci.md +173 -0
  28. package/dist/docs/versioned/next/how-to/configure-better-auth.md +13 -10
  29. package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
  30. package/dist/docs/versioned/next/how-to/install-agent-skills.md +46 -0
  31. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
  32. package/dist/docs/versioned/next/how-to/websocket-integration.md +2 -1
  33. package/dist/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
  34. package/dist/docs/versioned/next/implementationPlans/README.md +31 -17
  35. package/dist/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
  36. package/dist/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
  37. package/dist/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
  38. package/dist/docs/versioned/next/implementationPlans/admin-only.md +3 -1
  39. package/dist/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
  40. package/dist/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
  41. package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
  42. package/dist/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
  43. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
  44. package/dist/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
  45. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
  46. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
  47. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
  48. package/dist/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
  49. package/dist/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
  50. package/dist/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
  51. package/dist/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
  52. package/dist/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
  53. package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
  54. package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
  55. package/dist/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
  56. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
  57. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
  58. package/dist/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
  59. package/dist/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
  60. package/dist/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
  61. package/dist/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
  62. package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
  63. package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
  64. package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
  65. package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
  66. package/dist/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
  67. package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
  68. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
  69. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
  70. package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
  71. package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
  72. package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
  73. package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
  74. package/dist/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
  75. package/dist/docs/versioned/next/reference/README.md +6 -1
  76. package/dist/docs/versioned/next/reference/admin-config.md +51 -0
  77. package/dist/docs/versioned/next/reference/ai.md +15 -1
  78. package/dist/docs/versioned/next/reference/api.md +18 -0
  79. package/dist/docs/versioned/next/reference/comms.md +88 -7
  80. package/dist/docs/versioned/next/reference/environment-variables.md +9 -1
  81. package/dist/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
  82. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
  83. package/dist/docs/versioned/next/reference/mcp-server.md +7 -0
  84. package/dist/docs/versioned/next/reference/syncdb.md +30 -3
  85. package/dist/docs/versioned/next/tasks/README.md +21 -10
  86. package/dist/docs/versioned/next/tasks/admin-improvements.md +189 -353
  87. package/dist/docs/versioned/next/tasks/admin-only.md +2 -0
  88. package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
  89. package/dist/docs/versioned/next/tasks/comms-abstraction.md +30 -24
  90. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
  91. package/dist/docs/versioned/next/tasks/consent-forms.md +2 -0
  92. package/dist/docs/versioned/next/tasks/feature-flags.md +2 -0
  93. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
  94. package/dist/docs/versioned/next/tasks/model-router-actions.md +2 -0
  95. package/dist/docs/versioned/next/tasks/model-router-mcp.md +27 -27
  96. package/dist/docs/versioned/next/tasks/offline-mode.md +2 -0
  97. package/dist/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
  98. package/dist/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
  99. package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
  100. package/dist/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
  101. package/dist/docs/versioned/next/tasks/upgrade-banner.md +2 -0
  102. package/dist/index.js +24 -55
  103. package/dist/index.js.map +1 -1
  104. package/dist/local/localServer.d.ts.map +1 -1
  105. package/dist/local/localServer.js +9 -8
  106. package/dist/local/localServer.js.map +1 -1
  107. package/dist/local/localTools.d.ts +1 -1
  108. package/dist/local/localTools.d.ts.map +1 -1
  109. package/dist/prompts.d.ts.map +1 -1
  110. package/dist/prompts.js +44 -45
  111. package/dist/prompts.js.map +1 -1
  112. package/dist/resources.d.ts.map +1 -1
  113. package/dist/resources.js +9 -2
  114. package/dist/resources.js.map +1 -1
  115. package/dist/tools.d.ts +1 -1
  116. package/dist/tools.d.ts.map +1 -1
  117. package/dist/tools.js +4 -2
  118. package/dist/tools.js.map +1 -1
  119. package/package.json +7 -3
  120. package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
  121. package/src/docs/resources/overview.md +6 -1
  122. package/src/docs/resources/rtk.md +3 -1
  123. package/src/docs/resources/syncdb.md +43 -0
  124. package/src/docs/upgrades/56.0.0-beta.2.md +34 -0
  125. package/src/docs/upgrades/57.1.0.md +126 -0
  126. package/src/docs/versioned/next/README.md +15 -3
  127. package/src/docs/versioned/next/explanation/README.md +3 -0
  128. package/src/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
  129. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
  130. package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
  131. package/src/docs/versioned/next/explanation/authentication.md +51 -58
  132. package/src/docs/versioned/next/explanation/local-first-data.md +52 -0
  133. package/src/docs/versioned/next/explanation/loop-engineering.md +117 -0
  134. package/src/docs/versioned/next/explanation/repository-settings.md +8 -0
  135. package/src/docs/versioned/next/explanation/roadmap-process.md +22 -9
  136. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
  137. package/src/docs/versioned/next/how-to/README.md +3 -0
  138. package/src/docs/versioned/next/how-to/admin-add-model.md +41 -0
  139. package/src/docs/versioned/next/how-to/admin-custom-home.md +31 -0
  140. package/src/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
  141. package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
  142. package/src/docs/versioned/next/how-to/circleci.md +173 -0
  143. package/src/docs/versioned/next/how-to/configure-better-auth.md +13 -10
  144. package/src/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
  145. package/src/docs/versioned/next/how-to/install-agent-skills.md +46 -0
  146. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
  147. package/src/docs/versioned/next/how-to/websocket-integration.md +2 -1
  148. package/src/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
  149. package/src/docs/versioned/next/implementationPlans/README.md +31 -17
  150. package/src/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
  151. package/src/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
  152. package/src/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
  153. package/src/docs/versioned/next/implementationPlans/admin-only.md +3 -1
  154. package/src/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
  155. package/src/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
  156. package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
  157. package/src/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
  158. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
  159. package/src/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
  160. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
  161. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
  162. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
  163. package/src/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
  164. package/src/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
  165. package/src/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
  166. package/src/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
  167. package/src/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
  168. package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
  169. package/src/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
  170. package/src/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
  171. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
  172. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
  173. package/src/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
  174. package/src/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
  175. package/src/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
  176. package/src/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
  177. package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
  178. package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
  179. package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
  180. package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
  181. package/src/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
  182. package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
  183. package/src/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
  184. package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
  185. package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
  186. package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
  187. package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
  188. package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
  189. package/src/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
  190. package/src/docs/versioned/next/reference/README.md +6 -1
  191. package/src/docs/versioned/next/reference/admin-config.md +51 -0
  192. package/src/docs/versioned/next/reference/ai.md +15 -1
  193. package/src/docs/versioned/next/reference/api.md +18 -0
  194. package/src/docs/versioned/next/reference/comms.md +88 -7
  195. package/src/docs/versioned/next/reference/environment-variables.md +9 -1
  196. package/src/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
  197. package/src/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
  198. package/src/docs/versioned/next/reference/mcp-server.md +7 -0
  199. package/src/docs/versioned/next/reference/syncdb.md +30 -3
  200. package/src/docs/versioned/next/tasks/README.md +21 -10
  201. package/src/docs/versioned/next/tasks/admin-improvements.md +189 -353
  202. package/src/docs/versioned/next/tasks/admin-only.md +2 -0
  203. package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
  204. package/src/docs/versioned/next/tasks/comms-abstraction.md +30 -24
  205. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
  206. package/src/docs/versioned/next/tasks/consent-forms.md +2 -0
  207. package/src/docs/versioned/next/tasks/feature-flags.md +2 -0
  208. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
  209. package/src/docs/versioned/next/tasks/model-router-actions.md +2 -0
  210. package/src/docs/versioned/next/tasks/model-router-mcp.md +27 -27
  211. package/src/docs/versioned/next/tasks/offline-mode.md +2 -0
  212. package/src/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
  213. package/src/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
  214. package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
  215. package/src/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
  216. package/src/docs/versioned/next/tasks/upgrade-banner.md +2 -0
  217. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
  218. package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
  219. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
  220. package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
@@ -0,0 +1,194 @@
1
+ # Expose Model Context Protocol Tools
2
+
3
+ Turn a `modelRouter` model into Model Context Protocol (MCP) tools that an LLM can call, reusing the permissions, query filters, population, and lifecycle hooks you already declared for REST.
4
+
5
+ ## Prerequisites
6
+
7
+ - A working Terreno backend using `TerrenoApp`
8
+ - At least one model registered with `modelRouter`
9
+
10
+ ## Steps
11
+
12
+ ### 1. Add an `mcp` config to a model router
13
+
14
+ ```typescript
15
+ import {modelRouter, OwnerQueryFilter, Permissions} from "@terreno/api";
16
+
17
+ export const todoRouter = modelRouter("/todos", Todo, {
18
+ mcp: {
19
+ excludeFields: ["internalNote"],
20
+ maxLimit: 25,
21
+ methods: ["list", "read", "create", "update", "delete"],
22
+ },
23
+ permissions: {
24
+ create: [Permissions.IsAuthenticated],
25
+ delete: [Permissions.IsOwner],
26
+ list: [Permissions.IsAuthenticated],
27
+ read: [Permissions.IsOwner],
28
+ update: [Permissions.IsOwner],
29
+ },
30
+ queryFields: ["completed", "title"],
31
+ queryFilter: OwnerQueryFilter,
32
+ });
33
+ ```
34
+
35
+ `methods` defaults to `["list", "read"]`, and `maxLimit` defaults to 50. A method whose `permissions` array is empty is never exposed as a tool, so REST and MCP cannot drift apart.
36
+
37
+ ### 2. Start the server
38
+
39
+ `TerrenoApp` mounts `POST /mcp` automatically as soon as any model has an `mcp` config. Every other verb on `/mcp` returns a JSON-RPC 405. No extra registration is needed.
40
+
41
+ Tools are named `{prefix}_{method}` — `todos_list`, `todos_read`, and so on.
42
+
43
+ The endpoint speaks the `2026-07-28` MCP revision through the TypeScript SDK v2
44
+ `createMcpHandler`. It is stateless: every request carries its protocol version,
45
+ client identity, and capabilities, so requests can land on any backend instance.
46
+ The SDK's stateless legacy fallback remains enabled, so 2025-era clients continue
47
+ to work while clients migrate.
48
+
49
+ ### 3. Call the tools
50
+
51
+ Point any MCP client at `POST /mcp` with the user's `Authorization: Bearer <token>` header. Both JWT and Better Auth sessions are accepted; the resolved user is what the permission checks run against, so an LLM can never see more than that user could see over REST.
52
+
53
+ Authentication is required by default, matching REST. A tool call with no resolvable user is refused before any permission check unless the model router sets `allowAnonymous: true` — the same flag REST passes to `authenticateMiddleware`. That matters for read-only helpers like `IsAuthenticatedOrReadOnly`, which would otherwise pass for an anonymous `list`. Disabled accounts are refused too, as they are over HTTP.
54
+
55
+ To run the same tools in-process (for example inside a chat route), use `getMCPTools`:
56
+
57
+ ```typescript
58
+ import {getMCPTools} from "@terreno/ai";
59
+
60
+ const tools = getMCPTools(req.user);
61
+ ```
62
+
63
+ `useMCPTools()` from `@terreno/rtk` uses the official MCP v2 client with automatic
64
+ version negotiation. It no longer constructs JSON-RPC or parses SSE responses by
65
+ hand; the SDK supplies the `2026-07-28` `_meta` envelope and required
66
+ `Mcp-Method` / `Mcp-Name` routing headers.
67
+
68
+ ## Naming tools
69
+
70
+ The default prefix is the lowercase model name run through a simple pluralizer: `Todo` → `todos`, `Category` → `categories`, `Status` → `statuses`. Irregular nouns need an explicit prefix:
71
+
72
+ ```typescript
73
+ mcp: {toolPrefix: "people"} // person_list -> people_list
74
+ ```
75
+
76
+ ## Filtering list results
77
+
78
+ Only fields listed in `queryFields` can be filtered, exactly as in REST. A filter value may be an exact value or an operator object:
79
+
80
+ ```json
81
+ {"completed": false, "title": {"$in": ["Buy milk", "Walk dog"]}}
82
+ ```
83
+
84
+ Allowed operators are `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$all`, `$size`, `$exists`, and `$regex` (with `$options`). Operators that can execute code or ignore indexes — `$where`, `$expr`, `$function`, `$accumulator`, `$jsonSchema`, `$text` — are rejected with an error rather than silently dropped, so the model can correct itself.
85
+
86
+ Filters can be combined with top-level `$and` / `$or`, whose branches are validated against `queryFields` too:
87
+
88
+ ```json
89
+ {"$or": [{"completed": true}, {"title": {"$regex": "^Urgent"}}]}
90
+ ```
91
+
92
+ `queryFilter` still runs after the filter is built, so an `OwnerQueryFilter` keeps scoping results to the calling user no matter what the LLM sends.
93
+
94
+ ## Populating references
95
+
96
+ Population comes from the model router's `populatePaths`, exactly as it does over REST. A tool may pass `populate` to request a **subset** of those declared paths — it can never introduce a new one, so the `fields` allowlist on each declared path always applies and a caller cannot pull back a whole referenced document:
97
+
98
+ ```typescript
99
+ populatePaths: [{fields: ["name", "email"], path: "ownerId"}],
100
+ ```
101
+
102
+ `{"populate": "ownerId"}` is accepted; anything undeclared is refused with an error listing the allowed paths. Models with no `populatePaths` don't get a `populate` parameter at all.
103
+
104
+ ## Hiding fields
105
+
106
+ `excludeFields` removes fields from both the generated tool schemas and the responses:
107
+
108
+ - A bare name (`"hash"`) is removed at **every depth**, including inside arrays and populated refs, so a redacted name cannot leak through a nested document.
109
+ - A dot path (`"metadata.secretKey"`) removes exactly that location, following arrays of subdocuments element-wise.
110
+
111
+ ## Customizing responses
112
+
113
+ `mcpResponseHandler` shapes MCP responses independently of the REST `responseHandler`, which is useful for returning compact summaries to an LLM:
114
+
115
+ ```typescript
116
+ mcp: {
117
+ mcpResponseHandler: async (value, method) => {
118
+ if (Array.isArray(value)) {
119
+ return value.map((todo) => ({id: String(todo._id), title: todo.title}));
120
+ }
121
+ return {id: String(value._id), title: value.title};
122
+ },
123
+ }
124
+ ```
125
+
126
+ `excludeFields` is applied after your handler runs.
127
+
128
+ ## Lifecycle hooks
129
+
130
+ `preCreate`, `postCreate`, `preUpdate`, `postUpdate`, `preDelete`, and `postDelete` all run for MCP calls. An MCP tool call is JSON-RPC rather than HTTP, so `createMCPRequest` builds a stub Express-shaped `request` (`MCPRequest`):
131
+
132
+ - `user` — the authenticated MCP user
133
+ - `body` — the hook-stage payload after write denylist + transform, not the raw tool args (on delete, `id` is in `body`, not `params`)
134
+ - `headers` / `query` / `params` — always `{}` (MCP does not forward HTTP headers)
135
+ - `isMCPRequest` — `true`
136
+
137
+ REST `responseHandler` gets the same stub with an empty `body`.
138
+
139
+ ```typescript
140
+ import type {MCPRequest} from "@terreno/api";
141
+
142
+ preCreate: (body, req) => {
143
+ if ((req as unknown as MCPRequest).isMCPRequest) {
144
+ // e.g. tag records created by an assistant
145
+ return {...body, source: "assistant"};
146
+ }
147
+ return body;
148
+ },
149
+ ```
150
+
151
+ A hook that reads `req.headers` or `req.query` gets an empty object instead of a crash, but anything genuinely HTTP-specific (cookies, IP address) is unavailable — branch on `isMCPRequest` when that matters.
152
+
153
+ ## Logging and error reporting
154
+
155
+ Every generated tool call emits a structured backend audit log with stable
156
+ `mcpTool`, `mcpMethod`, and `mcpModel` labels plus the active Terreno request
157
+ context (`requestId`, `userId`, and trace fields when present):
158
+
159
+ - successful calls log at `info` with `durationMs`
160
+ - expected refusals (permissions, not found, invalid filters) log at `warn`
161
+ - caught database, transform, and lifecycle-hook failures log at `error`
162
+
163
+ When `USE_SENTRY_LOGGING=true`, expected failures are mirrored as Sentry logs.
164
+ Unexpected exceptions and caught internal causes are also captured with
165
+ `Sentry.captureException`, preserving their original stack. The client still
166
+ receives the stable MCP error text — internal exceptions and stack traces stay
167
+ server-side. Tool arguments are not logged.
168
+
169
+ ## Custom tools
170
+
171
+ CRUD tools cover one model at a time. For a query that spans models, register a custom tool with `registerMCPTool`. It shows up on `POST /mcp` and in `getMCPTools` next to the generated CRUD tools:
172
+
173
+ ```typescript
174
+ import {registerMCPTool} from "@terreno/api";
175
+ import {z} from "zod";
176
+
177
+ registerMCPTool({
178
+ name: "users_todo_statuses",
179
+ description: "List every user with each of their todos and completed status. Admin only.",
180
+ zodSchema: z.object({}).strict(),
181
+ handler: async (_args, user) => {
182
+ if (!user?.admin) {
183
+ return {
184
+ content: [{type: "text", text: JSON.stringify({error: "Permission denied: admin required"})}],
185
+ isError: true,
186
+ };
187
+ }
188
+ // load users + todos and return JSON text
189
+ return {content: [{type: "text", text: JSON.stringify({users: []})}]};
190
+ },
191
+ });
192
+ ```
193
+
194
+ The example backend registers this as `users_todo_statuses` from `example-backend/src/api/usersTodoStatus.ts`.
@@ -0,0 +1,46 @@
1
+ # Install agent skills
2
+
3
+ Install Terreno's agent skills into another repo or agent with the skills CLI.
4
+
5
+ ```bash
6
+ npx skills add FlourishHealth/terreno
7
+ npx skills add FlourishHealth/terreno --skill terreno-1-grow
8
+ ```
9
+
10
+ That copies the committed `skills/` tree: lifecycle stages (Grow, Pick, Roast, Brew,
11
+ Taste), repository skills, and published package skills.
12
+
13
+ ## What you get
14
+
15
+ | Group | Skills |
16
+ | --- | --- |
17
+ | Lifecycle | `terreno-1-grow` … `terreno-5-taste` |
18
+ | Terreno apps | backend, UI, data, schema, SDK |
19
+ | Docs | `update-docs`, `update-agent-docs`, architecture skills |
20
+ | GitHub | commit, PR, review, verify, release, deploy |
21
+
22
+ `skills.sh.json` at the repo root groups those names on [skills.sh](https://skills.sh).
23
+
24
+ ## Keep copies in sync (Terreno maintainers)
25
+
26
+ Canonical sources:
27
+
28
+ 1. `.rulesync/skills/` — repository skills (`bun run rules` generates agent copies)
29
+ 2. `plugins/terreno-planning/skills/` — portable lifecycle stages
30
+ 3. `<package>/.ai/skills/` — published package skills; these overlay the repo copies
31
+
32
+ Regenerate the installable tree:
33
+
34
+ ```bash
35
+ bun run skills:sync
36
+ bun run check:lifecycle-skills
37
+ ```
38
+
39
+ Do not hand-edit `skills/`.
40
+
41
+ ## Write human docs with the skills
42
+
43
+ Skills read architecture docs before changing code. After a user-visible or architectural
44
+ change, update `docs/` in the same slice using `update-docs`. See
45
+ [lifecycle plugin](../reference/lifecycle-plugin.md) and the
46
+ [documentation contract](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-planning/references/documentation-contract.md).
@@ -21,6 +21,8 @@ This guide is **AI-context-first**: it is written for an agent (or human) perfor
21
21
 
22
22
  The example app completed both steps; todos are syncdb-only while profile/admin still use generated RTK hooks.
23
23
 
24
+ **Validation (Task 3.5):** The profile screen (`example-frontend/app/(tabs)/profile.tsx`) was reviewed against this guide. `useGetMeQuery` / `usePatchMeMutation` are **non-synced** custom SDK routes — the guide §8 explicitly says to keep them on RTK. No profile migration is required; the guide is sufficient for that case.
25
+
24
26
  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
27
 
26
28
  ## 2. Install and configure
@@ -318,6 +320,18 @@ Required for Better Auth socket sessions. Legacy JWT sockets continue to work wi
318
320
 
319
321
  ## 8. Codegen
320
322
 
323
+ Synced collections use `bun run sync-sdk`, which runs `terreno-syncdb-codegen` from
324
+ `@terreno/syncdb`. It reads `/openapi.json`, finds list operations tagged
325
+ `x-terreno-sync`, and writes `store/syncDbSdk.ts` (`SYNC_COLLECTIONS` plus friendly
326
+ hooks such as `useTodos` / `useTodo` / `useCreateTodo`). Do not edit that file.
327
+ Custom collections call `createCollectionHooks` in a sibling file. Names are chosen
328
+ so they do not collide with RTK `openApiSdk.ts` during dual-stack migration.
329
+
330
+ ```bash
331
+ # Backend running on port 4000
332
+ cd example-frontend && bun run sync-sdk
333
+ ```
334
+
321
335
  ### Does `bun run sdk` still work?
322
336
 
323
337
  **Yes.** `example-frontend/package.json` still defines:
@@ -332,7 +346,7 @@ It runs `@rtk-query/codegen-openapi` against `openapi-config.ts` and writes `sto
332
346
 
333
347
  | Still generated | Stop using for migrated collections |
334
348
  |-----------------|-------------------------------------|
335
- | Auth routes, `GET /auth/me`, admin, AI explorer, version config, feature flags | `useGetTodosQuery`, `usePostTodosMutation`, etc. |
349
+ | Auth routes, `GET /auth/me`, admin, AI explorer, version config, feature flags | Collection CRUD — import from `store/syncDbSdk.ts` instead |
336
350
  | Custom REST / RPC endpoints | Todo `realtimeList` / `realtimeDocument` wiring in `store/sdk.ts` |
337
351
 
338
352
  Keep running `bun run sdk` after backend route changes — non-synced screens still import from `store/sdk.ts`.
@@ -342,8 +356,19 @@ Workflow:
342
356
  ```bash
343
357
  # Backend running on port 4000
344
358
  cd example-frontend && bun run sdk
359
+ cd example-frontend && bun run sync-sdk
345
360
  ```
346
361
 
362
+ ### Syncdb hooks codegen
363
+
364
+ For collections with `sync` enabled on the backend, generate local-first hooks from the same OpenAPI spec:
365
+
366
+ ```bash
367
+ cd example-frontend && bun run sync-sdk
368
+ ```
369
+
370
+ This writes `store/syncDbSdk.ts` with friendly hooks (`useTodos`, `useCreateTodo`, …) that intentionally differ from RTK names so both SDKs can coexist during migration. Import synced screens from `@/store/syncDbSdk`, not `store/sdk.ts`.
371
+
347
372
  ## 9. Feature flags
348
373
 
349
374
  Post-migration, feature flags stay on `@terreno/rtk` + OpenFeature — **not** on syncdb.
@@ -411,6 +411,7 @@ This logs:
411
411
 
412
412
  ## Related Documentation
413
413
 
414
- - [@terreno/rtk Reference](../reference/rtk.md) — Full API documentation
414
+ - [@terreno/syncdb Reference](../reference/syncdb.md) — Local-first data layer (replaces RTK for synced collections)
415
+ - [@terreno/rtk Reference (legacy)](../reference/legacy/rtk.md) — RTK Query utilities during the support window
415
416
  - [Authentication Architecture](../explanation/authentication.md) — JWT system deep-dive
416
417
  - [Socket.io Documentation](https://socket.io/docs/v4/) — Official Socket.io docs
@@ -1,5 +1,6 @@
1
1
  # Modular API Implementation Plan
2
2
 
3
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1084
3
4
  ## Overview
4
5
 
5
6
  This document outlines the implementation plan for a new modular API design in `@terreno/api`. The goal is to create a `TerrenoApp` class that provides a cleaner, more flexible interface for building APIs while maintaining backward compatibility with the existing `setupServer` function.
@@ -7,7 +7,7 @@ An implementation plan (IP) is one half of a two-file pair:
7
7
 
8
8
  | File | Holds | Path |
9
9
  | ---- | ----- | ---- |
10
- | **IP** | Design goals, architecture, models/APIs, phases, acceptance criteria | `docs/implementationPlans/<slug>.md` |
10
+ | **IP** | Design goals, architecture, models/APIs, testing decisions, phases, acceptance criteria | `docs/implementationPlans/<slug>.md` |
11
11
  | **Task list** | The bot-consumable execution checklist for that IP | `docs/tasks/<slug>.md` |
12
12
 
13
13
  The IP is the source of truth for *what and why*; the task list is the source of truth
@@ -15,11 +15,12 @@ for *the concrete steps*. External trackers (a GitHub roadmap issue, a Linear is
15
15
  discussion thread) link **to** these files — they never replace them. See
16
16
  [`docs/tasks/README.md`](../tasks/README.md) for the task-file shape.
17
17
 
18
- ## The live index
18
+ ## Status lives on each IP
19
19
 
20
- [`PLAN_INDEX.md`](PLAN_INDEX.md) is the authoritative list of active, completed, and
21
- deferred plans — read it, not this page, to see what is current. This README explains the
22
- process; `PLAN_INDEX.md` tracks the state.
20
+ There is no shared `PLAN_INDEX.md`. That file caused merge conflicts the same way a
21
+ shared changelog Unreleased section did. Status is the `**Status:**` line in each
22
+ `docs/implementationPlans/<slug>.md` (and in `archive/` after close). List those
23
+ directories to see what exists; this README explains the process.
23
24
 
24
25
  ## When to write an IP
25
26
 
@@ -35,34 +36,47 @@ Substantial work is planned before coding. Quick rule (full table in
35
36
 
36
37
  ## How a plan gets written and built
37
38
 
38
- The [`terreno-planning` plugin](../../plugins/README.md) drives the pipeline:
39
+ The [`terreno-planning` plugin](../../plugins/README.md) drives the pipeline.
39
40
 
40
- 1. **Blend** (`terreno-1-blend`) — write the IP + task list, questions first.
41
- 2. **Roast** (`terreno-2-roast`) — implement via TDD from the approved IP.
42
- 3. **Cupping** (`terreno-3-cupping`) — independently verify against the IP.
43
- 4. **Pour** (`terreno-4-pour`) — commit, push, open the PR.
44
- 5. **Dial In** (`terreno-5-dialin`) — drive CI and review until mergeable.
41
+ **Grow** interviews in grilling rounds (one frontier of decisions per message, recommended
42
+ answers, then wait). After the user confirms shared understanding it writes the two files
43
+ and ends with a **15-line verify table** — destination, in/out, tracer, task graph, test
44
+ seam — so a human can check the plan without rereading the IP.
45
+
46
+ For a small feature, an outer loop may use the plugin's **feature profile**: a compact
47
+ approved task contract, one fresh Pick invocation per frontier task, independent Roast,
48
+ then Brew and bounded Taste iterations. This preserves the former Grind behavior without
49
+ putting orchestration inside a lifecycle skill.
50
+
51
+ 1. **Grow** (`terreno-1-grow`) — grill, then write the IP + tracer-bullet task list; end with the 15-line verify table.
52
+ 2. **Pick** (`terreno-2-pick`) — implement one approved slice via TDD.
53
+ 3. **Roast** (`terreno-3-roast`) — independently verify the implementation against the IP.
54
+ 4. **Brew** (`terreno-4-brew`) — commit, push, open the PR.
55
+ 5. **Taste** (`terreno-5-taste`) — react once to current CI, mergeability, and review state.
56
+
57
+ The outer loop owns stage selection, execution-state persistence, waiting, retries, and
58
+ human/external escalation. Each stage emits the common machine-readable result described
59
+ in [`plugins/terreno-planning/references/lifecycle-contract.md`](../../plugins/terreno-planning/references/lifecycle-contract.md).
45
60
 
46
61
  You can also author an IP directly with the `ip` skill; both produce the same two files.
47
62
 
48
63
  ### Roadmap handoff (roadmap-enabled repos only)
49
64
 
50
65
  In repos that run a public roadmap (Discussions + a roadmap Project — currently Terreno),
51
- Blend hands off to `roadmap-item` once the IP is **Approved** to create or update the
66
+ Grow hands off to `roadmap-item` once the IP is **Approved** to create or update the
52
67
  public tracking issue, and the IP header records the `Discussion:` and `Roadmap issue:`
53
68
  links. See [`docs/explanation/roadmap-process.md`](../explanation/roadmap-process.md) for
54
69
  the full IP ↔ roadmap lifecycle.
55
70
 
56
71
  In repos with **no** roadmap board (Flourish, most consumer apps), the pipeline is
57
- identical minus that handoff: the IP + task list plus `PLAN_INDEX.md` are the source of
58
- truth, and Linear tracks execution via the IP header `Linear:` link.
72
+ identical minus that handoff: the IP + task list are the source of truth, and Linear
73
+ tracks execution via the IP header `Linear:` link.
59
74
 
60
75
  ## Lifecycle
61
76
 
62
- 1. **Draft** → **Approved** — IP written and reviewed (status lives in the IP header and
63
- `PLAN_INDEX.md`).
77
+ 1. **Draft** → **Approved** — IP written and reviewed (status lives in the IP header).
64
78
  2. **In progress** → **Complete** — implemented, verified, merged.
65
- 3. **Archive** — completed IPs move to `docs/implementationPlans/archive/` and the index is updated.
79
+ 3. **Archive** — completed IPs move to `docs/implementationPlans/archive/`.
66
80
 
67
81
  Plans that are fully implemented and now describe shipped architecture should be migrated
68
82
  to `docs/explanation/` as reference documentation.
@@ -0,0 +1,136 @@
1
+ # Research: Admin Improvements (post–Admin UI v2 gap)
2
+
3
+ **Date:** 2026-08-20
4
+ **PRD:** Implement what did not ship in Admin UI v2 — finish the `admin-improvements` architectural overhaul on top of the landed `admin-ui-v2-django-parity` work.
5
+
6
+ ## Scope investigated
7
+
8
+ - Existing IPs: `docs/implementationPlans/admin-improvements.md`, `docs/implementationPlans/admin-ui-v2-django-parity.md`, `docs/tasks/admin-improvements.md`, `docs/tasks/admin-ui-v2-django-parity.md`
9
+ - Packages: `api/`, `admin-backend/`, `admin-frontend/`, `admin-spa/`, `feature-flags/`, `example-backend/`, `example-frontend/`
10
+ - Roadmap: `.github/roadmap-fields.yml` present → roadmap handoff applies after IP approval
11
+
12
+ ## What Admin UI v2 already shipped
13
+
14
+ `admin-ui-v2-django-parity` is marked **Implemented** with all task phases complete. Evidence in-repo:
15
+
16
+ | Area | Shipped |
17
+ |------|---------|
18
+ | Config | `schemaVersion: 2`, extended model meta, `home.slots` (`navGlobal`, `contentTop`, `main`, `sidebar`) |
19
+ | Backend routes | `POST .../bulk-patch` (≤1000 ids), `POST /admin/background-tasks` |
20
+ | Scripts | `/admin/scripts/*`, `scripts[]` in config (see `admin-script-runner.md`) |
21
+ | Shell / home | `AdminShell`, `AdminHome` with built-in widget ids (`modelsGrid`, `scriptRunner`, `recentActivity`, `versionConfig`, …) |
22
+ | List | `AdminModelTable`: debounced search, inline typed filters, row select, declarative `actions` → bulk-patch or background enqueue |
23
+ | Form | `AdminModelForm`: fieldsets, readonly (server strips PATCH body) |
24
+ | Audit | `AdminAuditLog` + `onAdminAudit` + `recentActivity` widget (sidebar, last) |
25
+ | Integration | `admin-spa`, `example-backend` v2 demo registrations, Playwright e2e |
26
+
27
+ ## What `admin-improvements` planned but did NOT ship
28
+
29
+ All tasks in `docs/tasks/admin-improvements.md` remain **unchecked**. High-level gaps:
30
+
31
+ ### `@terreno/api` (Phase 1) — none landed
32
+
33
+ - No `api/src/adminTypes.ts` (`AdminConfig`, `AdminContribution`, `AdminAction`, …)
34
+ - No `admin?: AdminConfig` on `ModelRouterOptions`
35
+ - `TerrenoPlugin.register` still `(app, openApi?)` — no `terrenoApp` third arg
36
+ - No `getRegistrations()` / `getPlugins()` on `TerrenoApp`
37
+ - No `adminContribution?()` on `TerrenoPlugin`
38
+ - No typed `admin:model.changed` event bus on `TerrenoApp`
39
+ - No shared `scrubAdminFields` with populated-doc recursion
40
+ - `readonlyFields` / `excludeFields` enforcement lives in `admin-backend` only, not in `modelRouter` hooks
41
+
42
+ ### `@terreno/admin-backend` (Phase 2) — partial overlap with v2, architecture gap remains
43
+
44
+ | Planned | Status |
45
+ |---------|--------|
46
+ | Aggregate admin from modelRouter `admin:` + plugin contributions + legacy `models[]` | **Not done** — still `AdminApp({ models: [...] })` only |
47
+ | `legacy.ts` adapter + deprecation warnings | **Not done** |
48
+ | `filterParser.ts` (strict per-type `filter[k]=v`) | **Not done** — list uses `queryFields`-compatible filters directly |
49
+ | `actionRunner.ts` + `POST .../actions/:name` + frozen `AdminActionContext` | **Not done** — v2 uses `bulk-patch` + `background-tasks` with `patchKeys` |
50
+ | Per-user permission booleans in `/admin/config` | **Partial** — `permissions` are static config flags (`create/update/delete !== false`), not evaluated per requesting user |
51
+ | `adminFilter` async row scoping | **Not done** (only `queryFilter` on `AdminModelConfig`) |
52
+ | `capabilities`, `widgetIds` top-level config | **Not done** |
53
+ | `Cache-Control: private, no-store` on config | **Not verified** |
54
+
55
+ ### `@terreno/admin-frontend` (Phases 3–4) — v2 UI exists, provider/registry pattern missing
56
+
57
+ | Planned | Status |
58
+ |---------|--------|
59
+ | `AdminProvider` + three-bucket widget registry (`fields`, `home`, `screens`) | **Not done** |
60
+ | `AdminScreenRouter` | **Not done** — `example-frontend` still uses string-switch in `[model]/index.tsx` |
61
+ | Widget modules under `widgets/` | **Not done** — widgets inline in `AdminHome.tsx` |
62
+ | `AdminFilterDrawer` (right rail / mobile sheet) | **Not done** — filters inline in `AdminModelTable` |
63
+ | `AdminActionMenu` component | **Not done** — bulk UI inline in table |
64
+ | `AdminFieldRenderer` reads widgets from context | **Not done** — hardcoded switch |
65
+ | Plugin home widgets via registry (`feature-flags-overrides`) | **Warn-only** in `AdminHome` — no injectable component |
66
+
67
+ ### Pre-built plugin migrations (Phase 5) — not started
68
+
69
+ - `FeatureFlagsApp.adminContribution()` — still exports `featureFlagAdminConfig` passed manually in `example-backend`
70
+ - `ConsentApp.adminContribution()` — consent admins wired manually in server
71
+ - `DocumentStorageApp` / AI explorer — custom screens hand-rolled in example-frontend (`ADMIN_CUSTOM_SCREENS`, `AIAdminScreen`)
72
+
73
+ ### Docs / examples (Phase 6–7) — not started
74
+
75
+ - No `docs/how-to/admin-*.md` or `docs/reference/admin-config.md`
76
+ - Example apps not migrated to `admin:` on modelRouter / `AdminProvider` / `AdminScreenRouter`
77
+ - MCP `generate_route` still emits legacy patterns
78
+
79
+ ## Contract tensions (v2 vs original admin-improvements IP)
80
+
81
+ These need product decisions before rewriting the IP:
82
+
83
+ 1. **Bulk actions API**
84
+ - **v2:** declarative `actions[]` with `patchKeys` → `POST .../bulk-patch` or `background-tasks`
85
+ - **admin-improvements:** imperative `AdminAction.run(ctx)` → `POST .../actions/:name`
86
+ - Both can coexist or one can subsume the other.
87
+
88
+ 2. **Home layout config**
89
+ - **v2:** `home.slots` (Django template regions) with string widget ids
90
+ - **admin-improvements:** `home.widgets` array of typed specs (`{type:"modelStats"}`, plugin id strings, React components)
91
+ - Shipped contract is `slots`; IP must align to v2 unless breaking.
92
+
93
+ 3. **Filter wire format**
94
+ - **v2:** filters map to existing `queryFields` query params (kind-specific UI in table)
95
+ - **admin-improvements:** strict `filter[field]=value` parser module with injection hardening
96
+ - May be additive hardening on top of v2, or a new encoding.
97
+
98
+ 4. **Config field names**
99
+ - **v2:** kept `listFields`, `defaultSort`
100
+ - **admin-improvements:** originally considered `listDisplay` / `ordering` aliases — v2 added `listDisplay` alongside `listFields` in model meta
101
+
102
+ 5. **Per-user `/admin/config`**
103
+ - **admin-improvements:** evaluate permissions per request (uncacheable)
104
+ - **v2:** static permission flags on model config
105
+
106
+ ## Candidate delivery options (not chosen — for Step 3)
107
+
108
+ | Option | Scope | Tradeoff |
109
+ |--------|-------|----------|
110
+ | **A. Architecture-only** | Phases 1–2 + 5 + 6 (api types, aggregation, plugin contributions, docs); keep v2 UI/components as-is | Fastest path to “admin on modelRouter”; least UI churn |
111
+ | **B. Full original IP** | All phases including AdminProvider, ScreenRouter, filterParser, `/actions/:name`, drawer, docs, E2E | Largest diff; some v2 work duplicated or replaced |
112
+ | **C. Incremental slices** | Ship A first, then provider/registry (Phase 3), then optional hardening (filterParser, per-user config) | Lower risk; longer calendar time |
113
+
114
+ ## Decisions recorded (2026-08-20)
115
+
116
+ | # | Choice |
117
+ |---|--------|
118
+ | 1 | **B** — Full IP (provider, screen router, drawer, filterParser, plugins, docs, E2E) |
119
+ | 2 | **A** — Bulk actions: v2 `bulk-patch` only; no `/actions/:name` |
120
+ | 3 | **A** — `home.slots` canonical |
121
+ | 4 | **B** — Static permission flags on config |
122
+ | 5 | **A** — `filterParser` with v2-compatible wire shape |
123
+ | 6 | **A** — Plugin widgets in `admin-frontend/src/widgets/` |
124
+ | 7 | **A** — Atomic example-backend + frontend + admin-spa migration |
125
+ | 8 | **B** — New roadmap tracking issue |
126
+
127
+ IP and task list updated: `admin-improvements.md`, `docs/tasks/admin-improvements.md`.
128
+
129
+ ## References
130
+
131
+ - `docs/implementationPlans/admin-improvements.md` — original full IP (pre–v2)
132
+ - `docs/implementationPlans/admin-ui-v2-django-parity.md` — shipped v2 IP
133
+ - `docs/tasks/admin-improvements.md` — unchecked task list
134
+ - `example-backend/src/server.ts` — still uses `AdminApp({ models: [featureFlagAdminConfig, …] })`
135
+ - `example-frontend/app/admin/[model]/index.tsx` — manual screen routing
136
+ - `admin-frontend/src/AdminHome.tsx` — inline widgets, no `AdminProvider`