@terreno/mcp 57.3.0 → 57.4.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 (250) hide show
  1. package/README.md +7 -1
  2. package/dist/bootstrap.d.ts +0 -18
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +10 -2428
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
  7. package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
  8. package/dist/docs/resources/syncdb.md +4 -1
  9. package/dist/docs/upgrades/57.4.0.md +140 -0
  10. package/dist/docs/versioned/next/README.md +3 -0
  11. package/dist/docs/versioned/next/explanation/README.md +4 -1
  12. package/dist/docs/versioned/next/explanation/admin-interface.md +20 -13
  13. package/dist/docs/versioned/next/explanation/charts.md +29 -0
  14. package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
  15. package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  16. package/dist/docs/versioned/next/explanation/example-coverage.md +43 -0
  17. package/dist/docs/versioned/next/explanation/local-first-data.md +5 -2
  18. package/dist/docs/versioned/next/explanation/loop-engineering.md +9 -6
  19. package/dist/docs/versioned/next/explanation/model-router-actions.md +19 -2
  20. package/dist/docs/versioned/next/explanation/organizations.md +88 -0
  21. package/dist/docs/versioned/next/explanation/planning-plugins.md +114 -0
  22. package/dist/docs/versioned/next/explanation/positioning.md +3 -3
  23. package/dist/docs/versioned/next/explanation/repository-settings.md +6 -5
  24. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
  25. package/dist/docs/versioned/next/explanation/source-rules.md +1 -1
  26. package/dist/docs/versioned/next/explanation/static-analysis.md +76 -16
  27. package/dist/docs/versioned/next/how-to/README.md +10 -0
  28. package/dist/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
  29. package/dist/docs/versioned/next/how-to/add-organizations.md +204 -0
  30. package/dist/docs/versioned/next/how-to/admin-add-model.md +9 -1
  31. package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
  32. package/dist/docs/versioned/next/how-to/audit-log.md +85 -0
  33. package/dist/docs/versioned/next/how-to/background-jobs.md +296 -0
  34. package/dist/docs/versioned/next/how-to/build-admin-screens.md +42 -13
  35. package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
  36. package/dist/docs/versioned/next/how-to/circleci.md +136 -27
  37. package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
  38. package/dist/docs/versioned/next/how-to/configure-better-auth.md +4 -0
  39. package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
  40. package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
  41. package/dist/docs/versioned/next/how-to/github-actions-ci.md +4 -3
  42. package/dist/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
  43. package/dist/docs/versioned/next/how-to/in-app-notifications.md +147 -0
  44. package/dist/docs/versioned/next/how-to/install-agent-skills.md +24 -12
  45. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
  46. package/dist/docs/versioned/next/how-to/product-announcements.md +155 -0
  47. package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
  48. package/dist/docs/versioned/next/how-to/run-tests-locally.md +102 -0
  49. package/dist/docs/versioned/next/how-to/seed-a-database.md +7 -2
  50. package/dist/docs/versioned/next/implementationPlans/README.md +8 -5
  51. package/dist/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
  52. package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
  53. package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
  54. package/dist/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
  55. package/dist/docs/versioned/next/implementationPlans/announcements.md +289 -0
  56. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
  57. package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
  58. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
  59. package/dist/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
  60. package/dist/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
  61. package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
  62. package/dist/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
  63. package/dist/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
  64. package/dist/docs/versioned/next/implementationPlans/job-queues.md +330 -0
  65. package/dist/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
  66. package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
  67. package/dist/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
  68. package/dist/docs/versioned/next/implementationPlans/notification-center.md +185 -0
  69. package/dist/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
  70. package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
  71. package/dist/docs/versioned/next/reference/README.md +4 -0
  72. package/dist/docs/versioned/next/reference/admin-backend.md +83 -5
  73. package/dist/docs/versioned/next/reference/admin-config.md +7 -0
  74. package/dist/docs/versioned/next/reference/admin-frontend.md +178 -5
  75. package/dist/docs/versioned/next/reference/admin-spa.md +18 -1
  76. package/dist/docs/versioned/next/reference/ai.md +2 -0
  77. package/dist/docs/versioned/next/reference/announcements.md +179 -0
  78. package/dist/docs/versioned/next/reference/api.md +343 -4
  79. package/dist/docs/versioned/next/reference/comms.md +33 -0
  80. package/dist/docs/versioned/next/reference/create-terreno-app.md +44 -0
  81. package/dist/docs/versioned/next/reference/environment-variables.md +29 -0
  82. package/dist/docs/versioned/next/reference/jobs.md +319 -0
  83. package/dist/docs/versioned/next/reference/legacy/rtk.md +5 -1
  84. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
  85. package/dist/docs/versioned/next/reference/mcp-server.md +2 -43
  86. package/dist/docs/versioned/next/reference/scan-plugin.md +169 -0
  87. package/dist/docs/versioned/next/reference/syncdb.md +38 -12
  88. package/dist/docs/versioned/next/reference/test.md +57 -6
  89. package/dist/docs/versioned/next/reference/ui-performance.md +2 -1
  90. package/dist/docs/versioned/next/reference/ui.md +257 -3
  91. package/dist/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
  92. package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
  93. package/dist/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
  94. package/dist/docs/versioned/next/tasks/announcements.md +173 -0
  95. package/dist/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
  96. package/dist/docs/versioned/next/tasks/create-terreno-app.md +82 -0
  97. package/dist/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
  98. package/dist/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
  99. package/dist/docs/versioned/next/tasks/framework-audit-log.md +86 -0
  100. package/dist/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
  101. package/dist/docs/versioned/next/tasks/job-queues.md +144 -0
  102. package/dist/docs/versioned/next/tasks/knip-cleanup.md +146 -0
  103. package/dist/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
  104. package/dist/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
  105. package/dist/docs/versioned/next/tasks/notification-center.md +102 -0
  106. package/dist/docs/versioned/next/tasks/org-management-ui.md +14 -14
  107. package/dist/docs/versioned/next/tutorials/README.md +1 -1
  108. package/dist/docs/versioned/next/tutorials/getting-started.md +62 -22
  109. package/dist/help/excerpt.d.ts +2 -0
  110. package/dist/help/excerpt.d.ts.map +1 -0
  111. package/dist/help/excerpt.js +8 -0
  112. package/dist/help/excerpt.js.map +1 -0
  113. package/dist/help/updateNotes.d.ts +42 -0
  114. package/dist/help/updateNotes.d.ts.map +1 -0
  115. package/dist/help/updateNotes.js +235 -0
  116. package/dist/help/updateNotes.js.map +1 -0
  117. package/dist/index.js +1 -1
  118. package/dist/index.js.map +1 -1
  119. package/dist/local/tools/databaseQuery.d.ts +1 -0
  120. package/dist/local/tools/databaseQuery.d.ts.map +1 -1
  121. package/dist/local/tools/databaseQuery.js +1 -0
  122. package/dist/local/tools/databaseQuery.js.map +1 -1
  123. package/dist/packageGuidelines.d.ts +2 -2
  124. package/dist/packageGuidelines.d.ts.map +1 -1
  125. package/dist/packageGuidelines.js +2 -8
  126. package/dist/packageGuidelines.js.map +1 -1
  127. package/dist/resources.d.ts +3 -0
  128. package/dist/resources.d.ts.map +1 -1
  129. package/dist/resources.js +3 -0
  130. package/dist/resources.js.map +1 -1
  131. package/dist/search/chunker.d.ts +1 -0
  132. package/dist/search/chunker.d.ts.map +1 -1
  133. package/dist/search/chunker.js +1 -0
  134. package/dist/search/chunker.js.map +1 -1
  135. package/dist/search/docIndex.d.ts +1 -5
  136. package/dist/search/docIndex.d.ts.map +1 -1
  137. package/dist/search/docIndex.js +1 -0
  138. package/dist/search/docIndex.js.map +1 -1
  139. package/dist/search/docVersion.d.ts +2 -1
  140. package/dist/search/docVersion.d.ts.map +1 -1
  141. package/dist/search/docVersion.js +3 -1
  142. package/dist/search/docVersion.js.map +1 -1
  143. package/dist/tools.d.ts +2 -2
  144. package/dist/tools.d.ts.map +1 -1
  145. package/dist/tools.js +113 -2
  146. package/dist/tools.js.map +1 -1
  147. package/package.json +28 -27
  148. package/src/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
  149. package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
  150. package/src/docs/resources/syncdb.md +4 -1
  151. package/src/docs/upgrades/57.4.0.md +140 -0
  152. package/src/docs/versioned/next/README.md +3 -0
  153. package/src/docs/versioned/next/explanation/README.md +4 -1
  154. package/src/docs/versioned/next/explanation/admin-interface.md +20 -13
  155. package/src/docs/versioned/next/explanation/charts.md +29 -0
  156. package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
  157. package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  158. package/src/docs/versioned/next/explanation/example-coverage.md +43 -0
  159. package/src/docs/versioned/next/explanation/local-first-data.md +5 -2
  160. package/src/docs/versioned/next/explanation/loop-engineering.md +9 -6
  161. package/src/docs/versioned/next/explanation/model-router-actions.md +19 -2
  162. package/src/docs/versioned/next/explanation/organizations.md +88 -0
  163. package/src/docs/versioned/next/explanation/planning-plugins.md +114 -0
  164. package/src/docs/versioned/next/explanation/positioning.md +3 -3
  165. package/src/docs/versioned/next/explanation/repository-settings.md +6 -5
  166. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
  167. package/src/docs/versioned/next/explanation/source-rules.md +1 -1
  168. package/src/docs/versioned/next/explanation/static-analysis.md +76 -16
  169. package/src/docs/versioned/next/how-to/README.md +10 -0
  170. package/src/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
  171. package/src/docs/versioned/next/how-to/add-organizations.md +204 -0
  172. package/src/docs/versioned/next/how-to/admin-add-model.md +9 -1
  173. package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
  174. package/src/docs/versioned/next/how-to/audit-log.md +85 -0
  175. package/src/docs/versioned/next/how-to/background-jobs.md +296 -0
  176. package/src/docs/versioned/next/how-to/build-admin-screens.md +42 -13
  177. package/src/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
  178. package/src/docs/versioned/next/how-to/circleci.md +136 -27
  179. package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
  180. package/src/docs/versioned/next/how-to/configure-better-auth.md +4 -0
  181. package/src/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
  182. package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
  183. package/src/docs/versioned/next/how-to/github-actions-ci.md +4 -3
  184. package/src/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
  185. package/src/docs/versioned/next/how-to/in-app-notifications.md +147 -0
  186. package/src/docs/versioned/next/how-to/install-agent-skills.md +24 -12
  187. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
  188. package/src/docs/versioned/next/how-to/product-announcements.md +155 -0
  189. package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
  190. package/src/docs/versioned/next/how-to/run-tests-locally.md +102 -0
  191. package/src/docs/versioned/next/how-to/seed-a-database.md +7 -2
  192. package/src/docs/versioned/next/implementationPlans/README.md +8 -5
  193. package/src/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
  194. package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
  195. package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
  196. package/src/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
  197. package/src/docs/versioned/next/implementationPlans/announcements.md +289 -0
  198. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
  199. package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
  200. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
  201. package/src/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
  202. package/src/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
  203. package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
  204. package/src/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
  205. package/src/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
  206. package/src/docs/versioned/next/implementationPlans/job-queues.md +330 -0
  207. package/src/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
  208. package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
  209. package/src/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
  210. package/src/docs/versioned/next/implementationPlans/notification-center.md +185 -0
  211. package/src/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
  212. package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
  213. package/src/docs/versioned/next/reference/README.md +4 -0
  214. package/src/docs/versioned/next/reference/admin-backend.md +83 -5
  215. package/src/docs/versioned/next/reference/admin-config.md +7 -0
  216. package/src/docs/versioned/next/reference/admin-frontend.md +178 -5
  217. package/src/docs/versioned/next/reference/admin-spa.md +18 -1
  218. package/src/docs/versioned/next/reference/ai.md +2 -0
  219. package/src/docs/versioned/next/reference/announcements.md +179 -0
  220. package/src/docs/versioned/next/reference/api.md +343 -4
  221. package/src/docs/versioned/next/reference/comms.md +33 -0
  222. package/src/docs/versioned/next/reference/create-terreno-app.md +44 -0
  223. package/src/docs/versioned/next/reference/environment-variables.md +29 -0
  224. package/src/docs/versioned/next/reference/jobs.md +319 -0
  225. package/src/docs/versioned/next/reference/legacy/rtk.md +5 -1
  226. package/src/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
  227. package/src/docs/versioned/next/reference/mcp-server.md +2 -43
  228. package/src/docs/versioned/next/reference/scan-plugin.md +169 -0
  229. package/src/docs/versioned/next/reference/syncdb.md +38 -12
  230. package/src/docs/versioned/next/reference/test.md +57 -6
  231. package/src/docs/versioned/next/reference/ui-performance.md +2 -1
  232. package/src/docs/versioned/next/reference/ui.md +257 -3
  233. package/src/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
  234. package/src/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
  235. package/src/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
  236. package/src/docs/versioned/next/tasks/announcements.md +173 -0
  237. package/src/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
  238. package/src/docs/versioned/next/tasks/create-terreno-app.md +82 -0
  239. package/src/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
  240. package/src/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
  241. package/src/docs/versioned/next/tasks/framework-audit-log.md +86 -0
  242. package/src/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
  243. package/src/docs/versioned/next/tasks/job-queues.md +144 -0
  244. package/src/docs/versioned/next/tasks/knip-cleanup.md +146 -0
  245. package/src/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
  246. package/src/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
  247. package/src/docs/versioned/next/tasks/notification-center.md +102 -0
  248. package/src/docs/versioned/next/tasks/org-management-ui.md +14 -14
  249. package/src/docs/versioned/next/tutorials/README.md +1 -1
  250. package/src/docs/versioned/next/tutorials/getting-started.md +62 -22
@@ -0,0 +1,136 @@
1
+ # Implementation Plan: Remove the Knip baseline
2
+
3
+ **Status:** Complete
4
+ **Created:** 2026-09-09
5
+ **Owner:** unassigned
6
+ **Primary packages:** repo-wide (`knip.jsonc`, `scripts/static-analysis/`, every workspace Knip currently fingerprints)
7
+ **RTK deprecation flag:** None
8
+
9
+ ## Goal
10
+
11
+ `bun run analyze:full` stays green and Knip has no baseline file. Every finding that existed on 2026-09-08 (~1076 fingerprints: 529 default + 547 production) is either **gone from the live Knip report** (fixed) or **declared in `knip.jsonc` with a one-line reason** (disabled).
12
+
13
+ Inventory snapshot (do not treat as the live list — Pick re-runs Knip):
14
+
15
+ | Class | Default count (approx.) | Typical cause |
16
+ | --- | --- | --- |
17
+ | unused files | 123 | missing entries (`*.isolated.*`, `scripts/**/*.test.ts`, mcp tests, generated skill copies) |
18
+ | unused exports / types | 212 / 81 | internals still `export`ed; scripts libs not in graph; public API unused in-monorepo |
19
+ | unused dependencies | 62 | Expo autolink / Metro peers Knip cannot see; a few true leftovers |
20
+ | unused devDependencies | 34 | `sinon`, codegen CLIs, types for removed libs |
21
+ | unlisted | 13 | `expo-system-ui` in `app.json`, `jspdf` in metro stubs, optional `ioredis` |
22
+ | binaries / catalog / optional peers | 5 | `eas`, `maestro`, catalog `@sentry/react-native` |
23
+
24
+ ## Non-Goals
25
+
26
+ - Shrinking published `@terreno/*` public APIs (package `index` re-exports stay).
27
+ - Deleting Expo native, autolink, or Metro packages because Knip cannot see them.
28
+ - Running `knip --fix`.
29
+ - Changing dependency-cruiser rules except regenerating its baseline if a deleted file is listed there.
30
+ - Rewriting Biome or hook wiring.
31
+
32
+ ## Decisions
33
+
34
+ | Question | Decision |
35
+ |----------|----------|
36
+ | Knip baseline when done? | None. Survivors live only in `knip.jsonc`. |
37
+ | Unused published exports | Unexport or delete symbols **not** on the public package entry. Ignore leftover public API in `knip.jsonc`. |
38
+ | Expo / native `dependencies` Knip cannot see | `ignoreDependencies` / ignore globs with a reason. Do not remove from `package.json`. |
39
+ | Dead in-repo app/demo code | Delete or stop exporting when nothing in-repo uses it. Keep (then ignore as an entry) only what docs, E2E, or codegen still invoke. |
40
+ | Shape | One IP. Tasks by finding class, not one IP per package. |
41
+
42
+ ## Architecture
43
+
44
+ Knip has an incomplete **entry graph**. Most “unused files” are tests, isolated suites, Metro stubs, codegen configs, and generated skill copies that are never imported. Fix the graph first so later delete/unexport work is not guessing.
45
+
46
+ ```
47
+ knip.jsonc
48
+ entries → isolated tests, scripts tests, e2e, codegen, stubs
49
+ ignore* → Expo autolink, binaries, public API leftovers
50
+ code → delete dead files; unexport internal unused symbols
51
+ check:knip → fail on any finding not handled above
52
+ ```
53
+
54
+ ### Disposition rules (Pick must apply in this order)
55
+
56
+ 1. **Entry** — file is executed by Bun/Playwright/Expo/codegen/CI but Knip does not see it. Add `entry` / plugin / workspace config. Do not delete.
57
+ 2. **Ignore** — tool-blind (autolink, `app.json` plugins, optional native peers) **or** published public API unused in this monorepo. Add `ignore` / `ignoreDependencies` / `ignoreBinaries` / `ignoreIssues` with a comment naming the reason. Do not delete Expo native deps.
58
+ 3. **Unexport** — symbol is `export`ed from an internal module and is not on the package public entry. Drop `export` or move to a non-exported binding.
59
+ 4. **Delete** — file or dependency unused after (1)–(3), including in-repo-dead example/demo hooks and leftover `devDependencies` such as `sinon` once the graph is honest.
60
+
61
+ Never use `knip --fix`. Delete the old JSON baseline only after the live report is empty.
62
+
63
+ ### Public vs internal export
64
+
65
+ A symbol is **public** if a published package `src/index.ts` / `src/index.tsx` (or documented extra export path in that package’s `package.json` `exports`) re-exports it. All other unused exports are **internal**.
66
+
67
+ ### Expo / Metro ignore set (starting list; Pick verifies)
68
+
69
+ Apps (`demo`, `admin-spa`, `example-frontend`) and `ui` declare packages for autolinking, fonts, haptics, pickers, Skia, Metro polyfills (`crypto-browserify`, `stream-browserify`), and `jspdf` stubs. `app.json` / `app.config.ts` name `expo-system-ui` (and similar) without a JS import. Those stay in `package.json` and are ignored in Knip.
70
+
71
+ ## Models
72
+
73
+ None.
74
+
75
+ ## APIs
76
+
77
+ None. Operator surface is `knip.jsonc`, `bun run check:knip`, and `bun run analyze:full`.
78
+
79
+ ## Notifications
80
+
81
+ None.
82
+
83
+ ## UI
84
+
85
+ None. Example-frontend / demo file deletes must not remove screens still routed or documented. Frontend verification applies only to tasks that delete or unwire UI modules: launch the affected app, exercise the related screen, save artifacts.
86
+
87
+ ## Phases
88
+
89
+ | Phase | Outcome |
90
+ | --- | --- |
91
+ | 1 Entry graph | Isolated tests, `scripts/**/*.test.ts`, mcp tests, e2e, codegen, Metro stubs, generated skill script copies are entries or ignored globs. Unused-file count drops sharply. |
92
+ | 2 Documented ignores | Expo/autolink deps, binaries (`eas`, `maestro`), unlisted `app.json` plugins, catalog-only packages. |
93
+ | 3 True unused packages | Remove `devDependencies` / dependencies that are unused after the graph is honest (`sinon`, stale `@types/*`, etc.). |
94
+ | 4 Dead files | Delete in-repo-dead modules (example hooks, unused stories, unused scripts). |
95
+ | 5 Internal exports | Unexport unused internals. Ignore remaining public unused exports/types. |
96
+ | 6 Contract | Live Knip report empty. Delete the baseline. Rewrite the enforcement docs. |
97
+
98
+ Keep the existing fat baseline until Phase 6, then delete it once the live report is empty.
99
+
100
+ ## Feature Flags & Migrations
101
+
102
+ None.
103
+
104
+ ## Activity Log & User Updates
105
+
106
+ None.
107
+
108
+ ## Not Included / Future Work
109
+
110
+ - Teaching Knip Expo autolinking for real (upstream / custom compiler).
111
+ - Publishing previously unused public exports with in-repo callers (out of scope; ignore is enough).
112
+
113
+ ## Files to Create / Modify
114
+
115
+ | File | Role |
116
+ | --- | --- |
117
+ | `knip.jsonc` | Entries, ignores, workspace plugin fixes |
118
+ | `scripts/static-analysis/knip-baseline.json` | Delete after reaching zero findings |
119
+ | `scripts/static-analysis/full.ts` / `lib.test.ts` | Enforce zero findings directly |
120
+ | `docs/explanation/static-analysis.md` | Policy: no Knip baseline; ignore vs delete |
121
+ | Package `package.json` files | Remove true unused deps only |
122
+ | Dead source files listed by live Knip after Phase 1 | Delete or unexport |
123
+
124
+ ## Task List
125
+
126
+ [`docs/tasks/knip-cleanup.md`](../tasks/knip-cleanup.md)
127
+
128
+ ## Acceptance Criteria
129
+
130
+ - [ ] `bun run analyze:full` exits 0.
131
+ - [ ] `scripts/static-analysis/knip-baseline.json` does not exist.
132
+ - [ ] Every `ignore` / `ignoreDependencies` / `ignoreBinaries` / `ignoreIssues` entry in `knip.jsonc` has a comment stating why it cannot be a code fix.
133
+ - [ ] No published `@terreno/*` `index` export was removed.
134
+ - [ ] No Expo autolink/native package was removed from an app or `ui` `package.json` solely because Knip flagged it.
135
+ - [ ] `docs/explanation/static-analysis.md` describes the no-baseline policy and forbids `knip --fix`.
136
+ - [ ] `bun test scripts/static-analysis/lib.test.ts` still passes.
@@ -0,0 +1,210 @@
1
+ # Implementation Plan: Migrate built-in admin to syncdb
2
+
3
+ **Status:** Approved
4
+ **Branch:** `cursor/migrate-admin-to-syncdb-de61`
5
+ **Owner:** Josh Gachnang
6
+ **Created:** 2026-09-01
7
+ **Grow run:** https://cursor.com/agents/bc-47350376-6566-4c5a-aee2-e6a28698de61
8
+
9
+ ## Goal
10
+
11
+ Move **built-in admin model list/read/create/update/delete** for collections that already use **String `_id`** onto **windowed local-first syncdb**, with **REST remaining the membership source** (search, sort, pagination, RBAC). Remaining admin RPC (config, scripts, roles, comms, AI, consent, documents, version-config, background-tasks) leaves RTK `injectEndpoints` for a **tiny native `fetch` wrapper** with **host-injected auth**.
12
+
13
+ Ship **great characterization coverage and CI first**, then protocol, then UI, then hosts. Dual-run RTK `api` for one release so existing hosts keep working; **deprecate `api` / `injectEndpoints` in docs in this IP**; **delete the inject path in the next Terreno major**.
14
+
15
+ ## Non-Goals
16
+
17
+ - Migrating ObjectId `_id` models (`User`, `AdminAuditLog`, and any other ObjectId collection) onto syncdb. Those stay **fetch CRUD**. No schema migration in this IP.
18
+ - Axios. Admin HTTP uses native `fetch` plus a small wrapper.
19
+ - Full-stream `reconcile()` for admin collections. Admin subscribe is **window mode**: no snapshot of the entire owner/tenant/broadcast stream.
20
+ - A second `sync:` registration on the admin `modelRouter`. One sync registration per model stays on the **app** router (`/todos`, not `/admin/todos`).
21
+ - Changing app-user collections to broadcast. Users keep owner/tenant streams; admin is an extra fan-in (`adminBroadcast`).
22
+ - Removing `injectEndpoints` in this release (next major).
23
+ - Replacing generated OpenAPI SDK, Better Auth Redux, or feature-flag RTK with syncdb.
24
+
25
+ ## Decisions
26
+
27
+ | # | Question | Decision |
28
+ | --- | --- | --- |
29
+ | Q1 | What data plane does admin use? | String-`_id` **model CRUD** → **syncdb** (windowed). Remaining admin **RPC is not RTK**; dedicated **fetch** hooks. |
30
+ | Q2 | Coverage-first? | **A:** ≥95% line+function on admin packages; **fix CI coverage scope**; add embedded + SPA CRUD E2E **before** behavior migration. |
31
+ | Q3 | HTTP client | Native **`fetch` + tiny wrapper** (AbortController timeout, credentials, JSON/FormData, `APIError`). **No axios**. |
32
+ | Q4 | Auth injection | Host injects `{baseUrl, getAuthHeaders, credentials}`. SPA: `credentials: "same-origin"`. Embedded: Bearer. |
33
+ | Q5 | Local-first shape | **Windowed local-first:** load more hydrates more; keep loaded rows synced. |
34
+ | Q6 | Which models? | **B:** Sync only models that already have **String `_id`**. ObjectId models stay fetch CRUD. |
35
+ | Q7 | RTK dual-run | **B:** Dual-run **this release** (`api` still works). Delete RTK inject path **next major**. |
36
+ | Q8 | List membership vs store | **A:** REST list/search/sort/RBAC stays **membership**. Pages **upsert** into TinyBase. Writes + per-id via syncdb. **Full-stream reconcile OFF** for admin collections. |
37
+ | Q9 | Deltas vs new matches | **A + Refresh:** deltas **update/delete known ids only**. New matches via load-more or **visible Refresh**. No surprise inserts. |
38
+ | Q10 | Owner streams? | Admin needs a **cross-owner** view. Not the signed-in admin's owner stream. |
39
+ | Q11 | Dual-run vs cutover | Land **both** in this IP. **Deprecate `api` in docs now**. Remove `injectEndpoints` **next Terreno major**. |
40
+ | Q12 | Conflicts | **`ConflictSheet`** (`useServer` / `keepMine`) like the example app. |
41
+ | Q13 | Fan-in | **`adminBroadcast: true`** on the **existing** collection `SyncConfig`. Deltas also to `{collection}\|admin`. Admin clients join in **window** mode: no full snapshot/reconcile; hydrate via REST ids + `GET /sync/entities`. |
42
+
43
+ ## Architecture
44
+
45
+ ### Today
46
+
47
+ Admin CRUD is **RTK `injectEndpoints`** in `@terreno/admin-frontend` (`useAdminApi.ts` → `GET/POST/PATCH/DELETE /admin/{routePath}` plus bulk-patch). Config and other RPC are more inject hooks (`useAdminConfig`, scripts, roles, configuration, documents, comms, AI explorer, consent, version-config, background-tasks).
48
+
49
+ The backend mounts a **separate** `modelRouter` under `/admin/...` **without** `sync:`. Example `Todo` is already synced on the **app** router (`example-backend/src/api/todos.ts`) with String `_id` + `syncPlugin`. Same Mongo model, different mount. Registry allows **one sync registration per model**; a second `sync` on the admin router is forbidden (`api/src/sync/registrationSideEffects.ts`).
50
+
51
+ Canonical docs still say profile/admin stay on RTK (`docs/how-to/migrate-rtk-to-syncdb.md`).
52
+
53
+ ### Target data planes
54
+
55
+ | Surface | After this IP | Auth |
56
+ | --- | --- | --- |
57
+ | String-`_id` model changelist + form | REST membership + TinyBase rows + `{collection}\|admin` window subscribe | Host-injected (cookie or Bearer) via fetch for REST; existing sync socket auth for deltas/mutate |
58
+ | ObjectId model changelist + form | Fetch CRUD only (same `/admin/{routePath}` URLs) | Same fetch wrapper |
59
+ | Admin RPC (config, scripts, roles, comms, …) | Fetch hooks; **not** RTK | Same fetch wrapper |
60
+ | Host RTK `api` | Still accepted; dual-run. Deprecated in docs | Unchanged |
61
+
62
+ ### Windowed local-first (String `_id`)
63
+
64
+ 1. **Membership:** `GET /admin/{routePath}` (existing list/search/sort/RBAC) returns the **page of ids** (and enough fields to render immediately).
65
+ 2. **Hydrate:** those ids **upsert** into TinyBase. Missing entities come from `GET /sync/entities` (already exists) when the list payload is incomplete for sync seq / tombstones.
66
+ 3. **Subscribe:** admin client joins collection stream `{collection}|admin` in **window** mode. Socket handlers must **not** send a full snapshot or trigger client `reconcile()` of the whole user stream.
67
+ 4. **Deltas:** apply **update/delete only if the id is already in the local window**. Unknown ids are ignored (no surprise inserts).
68
+ 5. **Load more / Refresh:** load more requests the next REST page and hydrates those ids. **Refresh** re-runs the current REST query and upserts/removes to match membership (visible control).
69
+ 6. **Writes:** `useMutate` / `POST /sync/mutate` (same write path as the app) plus admin REST bulk-patch remaining on fetch until a later IP if bulk is not a sync mutation.
70
+
71
+ Do **not** set collection `scope` to broadcast for the app. App clients keep owner/tenant streams. `adminBroadcast: true` is an **additive fan-in**: change-stream emit also publishes to `{collection}|admin`. Only clients that subscribe in admin window mode join that stream.
72
+
73
+ ### Why not a second sync registration
74
+
75
+ `registerSync` throws if the model is already registered. Admin REST stays the **admin RBAC/list** surface. Sync stays on the **app** collection registration. Admin UI does not invent a second TinyBase table name; it uses the same collection tag as the app (`todos`), with an admin stream suffix.
76
+
77
+ ### Fetch wrapper
78
+
79
+ New module in `@terreno/admin-frontend` (not axios, not `emptyApi` JWT refresh). Responsibilities:
80
+
81
+ - `AbortController` timeout
82
+ - `credentials` (`same-origin` for SPA cookies; `omit`/`include` as injected)
83
+ - `getAuthHeaders()` for Bearer (embedded)
84
+ - JSON body by default; `FormData` passthrough
85
+ - Map non-2xx to `APIError` shape admin already understands
86
+
87
+ Hosts pass `{baseUrl, getAuthHeaders, credentials}` into `AdminProvider` / existing admin context. SPA must keep cookie session working; embedded example-frontend must keep Bearer.
88
+
89
+ ### Dual-run
90
+
91
+ `AdminModelTable` / `AdminModelForm` / related hooks:
92
+
93
+ - If `syncDb` + `adminRequest` (fetch client) are present **and** the model is String `_id` + registered for sync with `adminBroadcast`, use the new path.
94
+ - Else existing `useAdminApi(api, …)` RTK path.
95
+
96
+ No host is forced to pass `syncDb` this release. Docs tell new/updated hosts to inject both.
97
+
98
+ ### Conflicts
99
+
100
+ Same `ConflictSheet` as the example app: `useServer` / `keepMine`. Admin window does not invent a second conflict UX.
101
+
102
+ ## Models
103
+
104
+ No new Mongo models. No `_id` type changes.
105
+
106
+ | Model (example) | `_id` | This IP |
107
+ | --- | --- | --- |
108
+ | `Todo` | String | Tracer: windowed syncdb + adminBroadcast |
109
+ | Other app models already String `_id` + `sync` | String | Same pattern once `adminBroadcast: true` |
110
+ | `User`, `AdminAuditLog`, other ObjectId | ObjectId | Fetch CRUD only |
111
+
112
+ `SyncConfig` gains:
113
+
114
+ ```ts
115
+ adminBroadcast?: boolean;
116
+ ```
117
+
118
+ Default `false`. When `true`, emitters also send deltas to `{collection}|admin`. Permission to join that stream is **admin-only** (reuse existing admin permission checks used by `/admin/*`, not owner-stream membership).
119
+
120
+ ## APIs
121
+
122
+ ### Additive protocol
123
+
124
+ | Piece | Change |
125
+ | --- | --- |
126
+ | `SyncConfig.adminBroadcast` | Opt-in fan-in to `{collection}\|admin` |
127
+ | `sync:subscribe` | Accept window/admin mode (exact payload field named in Pick; suggested `{mode: "window"}` or subscribe to `{tag}\|admin` without snapshot) |
128
+ | `sync:subscribed` | Must not dump full stream snapshot for window subscribers |
129
+ | `GET /sync/entities` | Hydrate known ids (already exists; pin tests) |
130
+ | `GET /admin/{routePath}` | Unchanged membership/search/RBAC |
131
+ | `POST /sync/mutate` | Writes for String `_id` synced models (already exists) |
132
+
133
+ ### Unchanged REST (still used)
134
+
135
+ - `GET /admin/config`
136
+ - `/admin/{routePath}` list/read/create/update/delete/bulk-patch (membership + ObjectId CRUD + bulk)
137
+ - `/rbac/*`, scripts, configuration, documents, comms, AI, consent, version-config, background-tasks
138
+
139
+ ## Notifications
140
+
141
+ None. No user-facing comms templates.
142
+
143
+ ## UI
144
+
145
+ - **Changelist (`AdminModelTable`):** windowed rows from TinyBase for String `_id` synced models; REST pagination/search/sort unchanged; **Refresh** control; load more hydrates more ids; no rows appearing that were not in the last membership set.
146
+ - **Form (`AdminModelForm`):** read/write via syncdb for those models; ObjectId stays fetch.
147
+ - **Conflicts:** `ConflictSheet` when `useConflicts` reports for admin-loaded ids.
148
+ - **Hosts:** `example-frontend` `/admin` and `admin-spa` `/console` both wired for the tracer.
149
+ - Admin v2 `home.slots` unchanged.
150
+
151
+ ## Phases
152
+
153
+ 1. **Characterization + CI** — raise coverage, fix LCOV scope, add package CI jobs, pin URL goldens, add CRUD E2E. **No product behavior change.**
154
+ 2. **Fetch wrapper** — client + host injection; migrate RPC inject hooks onto fetch (dual-run until hosts pass the wrapper).
155
+ 3. **Protocol** — `adminBroadcast`, `{collection}|admin` emit, window subscribe, skip reconcile, known-id delta apply, entity hydrate tests.
156
+ 4. **Admin UI dual-run** — table/form/ConflictSheet/Refresh; ObjectId stays fetch.
157
+ 5. **Hosts** — example-frontend + admin-spa; cookie vs Bearer must both work.
158
+ 6. **Docs/skills** — stop saying admin collection CRUD stays RTK; deprecate `api` inject; changelog.
159
+
160
+ Phase 1 is a **hard gate**. Protocol/UI tasks are blocked until Phase 1 acceptance is green.
161
+
162
+ ## Feature Flags & Migrations
163
+
164
+ No feature flag. `adminBroadcast` is explicit per collection. Hosts opt into the new UI path by passing `syncDb` + fetch client.
165
+
166
+ **Deprecation:** this release documents “do not add new `injectEndpoints` in admin-frontend.” Next major deletes `useAdminApi` RTK inject and the `api` prop requirement.
167
+
168
+ ## Activity Log & User Updates
169
+
170
+ No end-user product changelog beyond admin operators. Framework changelog records protocol + deprecation.
171
+
172
+ ## Not Included / Future Work
173
+
174
+ - ObjectId → String `_id` + `syncPlugin` for User / audit log (own IP; mongoose-schema-safety).
175
+ - Delete RTK inject (next major).
176
+ - Bulk-patch as sync mutations.
177
+ - Admin-only collections with no app router (would still need one `sync:` registration, not a duplicate).
178
+
179
+ ## Files to Create / Modify
180
+
181
+ **Phase 1:** `admin-frontend/**/*.test.*`, `admin-frontend/bunfig.toml`, `admin-backend` coverage config, `.circleci/continue-config.yml`, root `package.json` (`admin-frontend:test`), `example-frontend/e2e/admin*.spec.ts`, `admin-spa/e2e/**`.
182
+
183
+ **Phase 2:** `admin-frontend/src/adminRequest.ts` (name at Pick), host context types, RPC hooks listed under Architecture.
184
+
185
+ **Phase 3:** `api/src/sync/types.ts`, `streams.ts`, change-stream emit, `socketHandlers.ts`, `syncdb/src/client.ts` + subscribe/reconcile/delta applier, tests in `api/src/sync/*.test.ts` and `syncdb/src/sync/*.test.ts`.
186
+
187
+ **Phase 4:** `AdminModelTable`, `AdminModelForm`, conflict wiring, `useAdminApi` dual-run.
188
+
189
+ **Phase 5:** `example-frontend` store/syncdb + admin layout; `admin-spa` session fetch + SyncDbProvider.
190
+
191
+ **Phase 6:** `docs/reference/admin-*.md`, `docs/reference/syncdb.md`, `docs/how-to/migrate-rtk-to-syncdb.md`, `docs/reference/admin-frontend.md` (if present), `terreno-data-fetching` skill (admin fetch exception), `verify-ui-changes`, changelog.
192
+
193
+ ## Task List
194
+
195
+ [docs/tasks/migrate-admin-to-syncdb.md](../tasks/migrate-admin-to-syncdb.md)
196
+
197
+ ## Acceptance Criteria
198
+
199
+ - [ ] `admin-frontend` and `admin-backend` report ≥95% line and function coverage on **package source** (not `@terreno/test/dist`). CircleCI runs both package jobs.
200
+ - [ ] Embedded `/admin` and SPA `/console` have CRUD E2E for Todos (list, create, edit, delete, search/filter, bulk where already specified) **before** protocol/UI cutover, and they still pass after cutover.
201
+ - [ ] Example **Todos** admin changelist is windowed local-first: REST membership, TinyBase rows, `{todos}|admin` deltas update known ids only, Refresh/load-more add membership, ConflictSheet works.
202
+ - [ ] ObjectId models still CRUD via fetch; no `_id` migration.
203
+ - [ ] SPA cookie auth and embedded Bearer auth both work; no axios in the new client.
204
+ - [ ] Hosts that still pass only `api` keep working (dual-run).
205
+ - [ ] Docs and skills no longer tell agents to keep admin **collection CRUD** on RTK; `api`/`injectEndpoints` marked deprecated; next-major removal called out.
206
+ - [ ] `bun run lint` and focused package tests pass; `verify-ui-changes` for `/admin` and `/console` tracer.
207
+
208
+ ## Tracer
209
+
210
+ Example **Todos** admin changelist: `example-frontend` `/admin` and `admin-spa` `/console`.
@@ -0,0 +1,254 @@
1
+ # MongoDB migrations tooling
2
+
3
+ **Status:** Approved — decisions 2026-09-10
4
+ **Branch:** `cursor/mongodb-migrations-tooling-04f9`
5
+ **Owner:** —
6
+ **Created:** 2026-09-10
7
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1189
8
+ **Primary packages:** `@terreno/api`, `@terreno/admin-backend`, `@terreno/admin-frontend`, `admin-spa`, `example-backend`
9
+
10
+ ## Goal
11
+
12
+ Give Terreno apps a **versioned MongoDB migration runner** in `@terreno/api`: timestamped `up` / optional `down` files, a **lock** so concurrent deploys cannot double-apply, a **`terreno-migrate` CLI** that **generates files from Mongoose schema diffs** and applies them, an **optional boot hook**, **CI checks**, and an **admin Migrations page** that reuses BackgroundTask / script-runner dry/wet — not the Scripts screen. Ad-hoc backfill scripts remain for one-off maintenance; versioned migrate is the path for schema/index/data changes that must run once per environment.
13
+
14
+ ## Non-Goals
15
+
16
+ - A separate `@terreno/migrations` package.
17
+ - Framework-shipped migration packs (legacy SyncApp index drops stay in framework startup code).
18
+ - Per-file apply/rollback in the admin UI (v1 is batch pending, like `migrate up`).
19
+ - SQLite / cross-adapter data migration (see [`pluggable-database-sqlite.md`](pluggable-database-sqlite.md)).
20
+ - Fully automatic data backfills for required fields, unique indexes, or renames.
21
+ - Replacing `AdminApp` scripts or `runSeedCli`.
22
+ - Auto `runOnStart` (the hook exists, default off).
23
+
24
+ ## Decisions
25
+
26
+ | ID | Question | Choice |
27
+ |----|----------|--------|
28
+ | Q1 | Delivery surface | `@terreno/api` exports + `terreno-migrate` CLI (generate from diffs) |
29
+ | Q2 | File layout | `migrations/<timestamp>-<name>.ts` with `up` and optional `down` |
30
+ | Q3 | Production wet apply | `NODE_ENV=production` requires `ALLOW_MIGRATIONS=true`. CLI also needs `--force`. Boot `runOnStart: true` and admin Apply are the force equivalent. |
31
+ | Q4 | Rollback | `down` optional; `migrate down` errors clearly when the target file has no `down` |
32
+ | Q5 | CI | Validate module load + filename order; on a fresh DB run `up` then `down` for files that export `down` |
33
+ | Q6 | Generate source | Current Mongoose models vs last committed `schemaAfter` snapshot in prior generated files (Django `makemigrations`) |
34
+ | Q7 | When `up` runs | CLI always; optional `TerrenoApp` `migrations.runOnStart` (default false) |
35
+ | Q8 | Lock | Wait, heartbeat, steal after **10 minutes** stale TTL |
36
+ | Q9 | Admin | Separate Migrations page; reuse script-runner / `BackgroundTask`; dry + wet |
37
+ | Q10 | Framework packs | App `migrations/` only in v1 |
38
+ | Q11 | Boot gate | `runOnStart: true` plus `ALLOW_MIGRATIONS=true` in production |
39
+ | Q12 | Admin wet in prod | Same `ALLOW_MIGRATIONS` gate as CLI |
40
+ | Q13 | Dry-run | Call pending `up({dryRun: true})`; authors must no-op writes |
41
+ | Q14 | Admin actions | Status + dry-run pending + apply pending as **one** BackgroundTask |
42
+ | Q15 | Generator output | Indexes and safe additive ops generated; required / unique / rename emit a **fail-closed stub** until the author fills the backfill |
43
+
44
+ **Assumptions (not grilled):** production means `NODE_ENV === "production"`; bin name `terreno-migrate` (same pattern as `terreno-syncdb-codegen`); history+lock live in Mongo collection `terreno_migrations`; seeds must not wipe that collection (already called out in [`seed-a-database.md`](../how-to/seed-a-database.md)).
45
+
46
+ ## Architecture
47
+
48
+ ```
49
+ models (Mongoose)
50
+ │
51
+ ├─ terreno-migrate generate
52
+ │ vs last schemaAfter snapshot
53
+ │ → migrations/<ts>-<name>.ts (+ schemaAfter)
54
+ │
55
+ ├─ terreno-migrate up|down|status [--dry]
56
+ │ lock → apply → record
57
+ │
58
+ ├─ TerrenoApp({migrations: {dir, models, runOnStart}})
59
+ │ optional up on listen (same runner)
60
+ │
61
+ └─ Admin
62
+ GET {base}/migrations/status collection action (applied / pending)
63
+ POST {base}/migrations/run?wetRun=
64
+ collection action → BackgroundTask (batch pending up)
65
+ GET/DELETE …/scripts/tasks/:id reuse existing poll/cancel
66
+ ```
67
+
68
+ ### Migration module contract
69
+
70
+ ```typescript
71
+ export interface MigrationContext {
72
+ dryRun: boolean;
73
+ mongoose: typeof import("mongoose");
74
+ logger: {info: ...; warn: ...; error: ...; debug: ...};
75
+ addLog?: (level, message) => Promise<void>;
76
+ checkCancellation?: () => Promise<void>;
77
+ }
78
+
79
+ export interface MigrationModule {
80
+ id: string; // must match filename without .ts
81
+ up: (ctx: MigrationContext) => Promise<void>;
82
+ down?: (ctx: MigrationContext) => Promise<void>;
83
+ /** Present on generated files; omitted on hand-written data migrations. */
84
+ schemaAfter?: SchemaCatalog;
85
+ }
86
+ ```
87
+
88
+ `loadMigrations({dir})` sorts by filename (`YYYYMMDDHHmmss-slug.ts`). Duplicate ids or a filename/id mismatch fail validation.
89
+
90
+ ### History and lock
91
+
92
+ Collection `terreno_migrations` (not exposed on `modelRouter`):
93
+
94
+ | `_id` | Role |
95
+ |-------|------|
96
+ | `"_lock"` | `{holder, heartbeatAt, expiresAt}` |
97
+ | `<migration id>` | `{id, checksum, appliedAt, appliedBy}` |
98
+
99
+ Lock acquire: if missing or `expiresAt` in the past, take it; else wait (poll ~1s) until expiry, then steal. Holder heartbeats while `up`/`down` runs. Always release in `finally`.
100
+
101
+ Checksum is a stable hash of the file source. `up` of an already-applied id with a **different** checksum is a hard error (edited after apply).
102
+
103
+ ### Production gate
104
+
105
+ `assertMigrationsAllowed({isProduction, allowEnv, force})`:
106
+
107
+ | Environment | Dry-run | Wet |
108
+ |-------------|---------|-----|
109
+ | non-prod | allowed | allowed |
110
+ | prod, `ALLOW_MIGRATIONS` unset | allowed | deny |
111
+ | prod, env set, no force | allowed | deny (CLI) |
112
+ | prod, env set, force (`--force` / `runOnStart` / admin Apply) | allowed | allowed |
113
+
114
+ Admin status and dry-run never require the env gate. Admin wet in prod requires the env (Apply is force).
115
+
116
+ ### Generator (Q6 + Q15)
117
+
118
+ `generate({models, dir})`:
119
+
120
+ 1. Build `SchemaCatalog` from registered Mongoose models (paths, types, required, unique, indexes — not methods/virtuals).
121
+ 2. Load last `schemaAfter` among sorted files (hand-written files without snapshot are skipped for the baseline).
122
+ 3. Diff: add/remove/change index; add optional field; add required field; unique index; rename heuristic (remove+add same type).
123
+ 4. Emit one new file:
124
+ - **Safe:** `createIndex` / `dropIndex` / no-op field comments for optional adds (Mongo is schemaless; optional add is snapshot-only unless an index is involved).
125
+ - **Unsafe:** required without default, unique, rename → `up` throws unless the author replaces the stub (and `dryRun` still throws so CI fails closed).
126
+ 5. Write `schemaAfter` of the **current** catalog so the next generate is incremental.
127
+ 6. No-op diff → exit 0, no file.
128
+
129
+ Do **not** inspect the live database during generate.
130
+
131
+ ### CLI
132
+
133
+ `@terreno/api` `"bin": {"terreno-migrate": "./src/migrations/cli.ts"}` (`#!/usr/bin/env bun`).
134
+
135
+ ```text
136
+ terreno-migrate generate --dir ./migrations --models ./src/models.ts
137
+ terreno-migrate status --dir ./migrations
138
+ terreno-migrate up --dir ./migrations [--dry] [--force]
139
+ terreno-migrate down --dir ./migrations [--dry] [--force] [--steps N]
140
+ terreno-migrate check --dir ./migrations # CI: validate files (no Mongo)
141
+ ```
142
+
143
+ `--models` is a module that exports models (side-effect register) or `{models: Model[]}`. `up`/`down`/`status` need `MONGO_URI` (or `MONGODB_URI`).
144
+
145
+ ### Boot hook
146
+
147
+ ```typescript
148
+ new TerrenoApp({
149
+ userModel: User,
150
+ migrations: {
151
+ dir: "./migrations",
152
+ models, // for generate-at-runtime not required; used if runOnStart
153
+ runOnStart: false, // default
154
+ },
155
+ });
156
+ ```
157
+
158
+ When `runOnStart: true`, `start()`/`build()` runs wet `up` after Mongo is connected and **before** listen. Production still requires `ALLOW_MIGRATIONS=true`. Failure aborts boot.
159
+
160
+ ### Admin (reuse script runner, not Scripts)
161
+
162
+ - Config: `GET /admin/config` includes `migrations?: {enabled: boolean}` when `AdminApp` is given a migration loader (same `dir` as the app).
163
+ - Routes (IsAdmin, authenticate):
164
+ - `GET {base}/migrations` → `{applied, pending, lock}`
165
+ - `POST {base}/migrations/run?wetRun=true|false` → `{taskId}` ; runner calls batch `up` with `dryRun: !wetRun`. Prod wet requires `ALLOW_MIGRATIONS`.
166
+ - Task poll/cancel: **existing** `/scripts/tasks/:id` (same `BackgroundTask`).
167
+ - UI: nav item **Migrations** (`__migrations`), not `__scripts`. List applied/pending; Dry run / Apply pending; poll logs via `useAdminScripts` task endpoints or a thin `useAdminMigrations` wrapper. No per-file buttons.
168
+
169
+ ### CI (Q5)
170
+
171
+ Export `checkMigrationFiles({dir})` (order, ids, load) and `exerciseReversibleMigrations({dir, connect})` (fresh DB, `up` all, `down` only files with `down`, in reverse, until an irreversible file stops the chain with a recorded skip).
172
+
173
+ `example-backend` test uses the in-memory Mongo harness. Optional `bun run migrate:check` script.
174
+
175
+ ## Models
176
+
177
+ No public CRUD model. Internal `terreno_migrations` documents only. Follow mongoose-schema-safety for any schema we do persist (descriptions, plugins only if we use a Mongoose model; a native collection is acceptable to keep this off `checkModelsStrict` consumer lists).
178
+
179
+ ## APIs
180
+
181
+ | Method | Path | Purpose |
182
+ |--------|------|---------|
183
+ | GET | `{adminBase}/migrations` | Status |
184
+ | POST | `{adminBase}/migrations/run` | Batch pending; `wetRun` query |
185
+
186
+ Programmatic: `runMigrations`, `loadMigrations`, `generateMigration`, `checkMigrationFiles`, `assertMigrationsAllowed`.
187
+
188
+ ## Notifications
189
+
190
+ None.
191
+
192
+ ## UI
193
+
194
+ Admin-spa + admin-frontend: Migrations screen and shell nav. `verify-ui-changes` is mandatory for that task.
195
+
196
+ ## Phases
197
+
198
+ 1. Runner + lock + history + dryRun (programmatic, no CLI).
199
+ 2. File loader + CLI `status`/`up`/`down`/`check` + production gate.
200
+ 3. `generate` + schema catalog + fail-closed stubs.
201
+ 4. `TerrenoApp` `runOnStart`.
202
+ 5. Admin HTTP.
203
+ 6. Admin UI + spa route.
204
+ 7. example-backend files, CI helper usage, docs, mongoose-schema-safety, changelog.
205
+
206
+ ## Feature Flags & Migrations
207
+
208
+ No feature flag. Opt-in via `migrations` option / CLI. Existing apps unchanged until they add a `migrations/` directory.
209
+
210
+ ## Activity Log & User Updates
211
+
212
+ Admin Apply creates a `BackgroundTask` (`taskType: "migrations:up"`) with logs; no end-user notifications.
213
+
214
+ ## Not Included / Future Work
215
+
216
+ - Framework migration pack for SyncApp legacy indexes.
217
+ - Per-migration admin rollback.
218
+ - Live-DB drift command (`schema` vs Mongo indexes).
219
+ - SQLite DDL migrations (owned by the SQLite adapter IP).
220
+
221
+ ## Files to Create / Modify
222
+
223
+ **Create**
224
+
225
+ - `api/src/migrations/` — types, lock, runner, loader, generate, cli, tests
226
+ - `docs/how-to/run-mongodb-migrations.md`
227
+ - `example-backend/migrations/` — at least one reversible example + one generated snapshot baseline
228
+ - `admin-frontend/src/AdminMigrations.tsx` (+ tests)
229
+ - `admin-spa/app/migrations.tsx` (or `__migrations` segment)
230
+
231
+ **Modify**
232
+
233
+ - `api/package.json` `bin`, `api/src/index.ts`, `api/src/terrenoApp.ts`
234
+ - `admin-backend/src/adminApp.ts` + tests
235
+ - `admin-frontend` shell, router, config types
236
+ - `docs/reference/api.md`, `docs/reference/environment-variables.md`, `docs/reference/admin-config.md`, `docs/how-to/create-a-model.md` or mongoose-schema-safety skill, `docs/how-to/seed-a-database.md` (do not reset `terreno_migrations`)
237
+ - `plugins/terreno-planning/skills/mongoose-schema-safety/SKILL.md` (and `bun run skills:sync`)
238
+ - `CHANGELOG.md`
239
+
240
+ ## Task List
241
+
242
+ [docs/tasks/mongodb-migrations-tooling.md](../tasks/mongodb-migrations-tooling.md)
243
+
244
+ ## Acceptance Criteria
245
+
246
+ - [ ] `loadMigrations` + `runMigrations({dryRun})` apply pending ids once, persist history, honor `dryRun` (no history write on dry-run).
247
+ - [ ] Concurrent `up` uses the lock; a stale lock older than 10 minutes is stolen.
248
+ - [ ] Production wet CLI without `ALLOW_MIGRATIONS` or `--force` exits non-zero; dry-run still works.
249
+ - [ ] `down` of a module without `down` fails with an actionable error; modules with `down` reverse on a fresh DB in CI.
250
+ - [ ] `generate` writes a timestamped file from model-vs-snapshot diff; required/unique/rename stubs throw until edited.
251
+ - [ ] `runOnStart: true` applies pending before listen; default omit does not.
252
+ - [ ] Admin Migrations page is not Scripts; dry-run and Apply pending use BackgroundTask; prod Apply without env is 403.
253
+ - [ ] example-backend can `bun run migrate:up` against the test DB; docs describe generate → dry-run → up.
254
+ - [ ] mongoose-schema-safety tells authors to add a versioned migration instead of a one-off script for schema backfills.