@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.
- package/README.md +7 -1
- package/dist/bootstrap.d.ts +0 -18
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +10 -2428
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/dist/docs/resources/syncdb.md +4 -1
- package/dist/docs/upgrades/57.4.0.md +140 -0
- package/dist/docs/versioned/next/README.md +3 -0
- package/dist/docs/versioned/next/explanation/README.md +4 -1
- package/dist/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/dist/docs/versioned/next/explanation/charts.md +29 -0
- package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/dist/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/dist/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/dist/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/dist/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/dist/docs/versioned/next/explanation/organizations.md +88 -0
- package/dist/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/dist/docs/versioned/next/explanation/positioning.md +3 -3
- package/dist/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/dist/docs/versioned/next/explanation/source-rules.md +1 -1
- package/dist/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/dist/docs/versioned/next/how-to/README.md +10 -0
- package/dist/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/dist/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/dist/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/dist/docs/versioned/next/how-to/audit-log.md +85 -0
- package/dist/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/dist/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/dist/docs/versioned/next/how-to/circleci.md +136 -27
- package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/dist/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/dist/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/dist/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/dist/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/dist/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/dist/docs/versioned/next/implementationPlans/README.md +8 -5
- package/dist/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/dist/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/dist/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/dist/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/dist/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/dist/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/dist/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/dist/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/dist/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/dist/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/dist/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/dist/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/dist/docs/versioned/next/reference/README.md +4 -0
- package/dist/docs/versioned/next/reference/admin-backend.md +83 -5
- package/dist/docs/versioned/next/reference/admin-config.md +7 -0
- package/dist/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/dist/docs/versioned/next/reference/admin-spa.md +18 -1
- package/dist/docs/versioned/next/reference/ai.md +2 -0
- package/dist/docs/versioned/next/reference/announcements.md +179 -0
- package/dist/docs/versioned/next/reference/api.md +343 -4
- package/dist/docs/versioned/next/reference/comms.md +33 -0
- package/dist/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +29 -0
- package/dist/docs/versioned/next/reference/jobs.md +319 -0
- package/dist/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/dist/docs/versioned/next/reference/mcp-server.md +2 -43
- package/dist/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/dist/docs/versioned/next/reference/syncdb.md +38 -12
- package/dist/docs/versioned/next/reference/test.md +57 -6
- package/dist/docs/versioned/next/reference/ui-performance.md +2 -1
- package/dist/docs/versioned/next/reference/ui.md +257 -3
- package/dist/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/dist/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/dist/docs/versioned/next/tasks/announcements.md +173 -0
- package/dist/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/dist/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/dist/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/dist/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/dist/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/dist/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/dist/docs/versioned/next/tasks/job-queues.md +144 -0
- package/dist/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/dist/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/dist/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/dist/docs/versioned/next/tasks/notification-center.md +102 -0
- package/dist/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/dist/docs/versioned/next/tutorials/README.md +1 -1
- package/dist/docs/versioned/next/tutorials/getting-started.md +62 -22
- package/dist/help/excerpt.d.ts +2 -0
- package/dist/help/excerpt.d.ts.map +1 -0
- package/dist/help/excerpt.js +8 -0
- package/dist/help/excerpt.js.map +1 -0
- package/dist/help/updateNotes.d.ts +42 -0
- package/dist/help/updateNotes.d.ts.map +1 -0
- package/dist/help/updateNotes.js +235 -0
- package/dist/help/updateNotes.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local/tools/databaseQuery.d.ts +1 -0
- package/dist/local/tools/databaseQuery.d.ts.map +1 -1
- package/dist/local/tools/databaseQuery.js +1 -0
- package/dist/local/tools/databaseQuery.js.map +1 -1
- package/dist/packageGuidelines.d.ts +2 -2
- package/dist/packageGuidelines.d.ts.map +1 -1
- package/dist/packageGuidelines.js +2 -8
- package/dist/packageGuidelines.js.map +1 -1
- package/dist/resources.d.ts +3 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +3 -0
- package/dist/resources.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -0
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/chunker.js +1 -0
- package/dist/search/chunker.js.map +1 -1
- package/dist/search/docIndex.d.ts +1 -5
- package/dist/search/docIndex.d.ts.map +1 -1
- package/dist/search/docIndex.js +1 -0
- package/dist/search/docIndex.js.map +1 -1
- package/dist/search/docVersion.d.ts +2 -1
- package/dist/search/docVersion.d.ts.map +1 -1
- package/dist/search/docVersion.js +3 -1
- package/dist/search/docVersion.js.map +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +113 -2
- package/dist/tools.js.map +1 -1
- package/package.json +28 -27
- package/src/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/src/docs/resources/syncdb.md +4 -1
- package/src/docs/upgrades/57.4.0.md +140 -0
- package/src/docs/versioned/next/README.md +3 -0
- package/src/docs/versioned/next/explanation/README.md +4 -1
- package/src/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/src/docs/versioned/next/explanation/charts.md +29 -0
- package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/src/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/src/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/src/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/src/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/src/docs/versioned/next/explanation/organizations.md +88 -0
- package/src/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/src/docs/versioned/next/explanation/positioning.md +3 -3
- package/src/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/src/docs/versioned/next/explanation/source-rules.md +1 -1
- package/src/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/src/docs/versioned/next/how-to/README.md +10 -0
- package/src/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/src/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/src/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/src/docs/versioned/next/how-to/audit-log.md +85 -0
- package/src/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/src/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/src/docs/versioned/next/how-to/circleci.md +136 -27
- package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/src/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/src/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/src/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/src/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/src/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/src/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/src/docs/versioned/next/implementationPlans/README.md +8 -5
- package/src/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/src/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/src/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/src/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/src/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/src/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/src/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/src/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/src/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/src/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/src/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/src/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/src/docs/versioned/next/reference/README.md +4 -0
- package/src/docs/versioned/next/reference/admin-backend.md +83 -5
- package/src/docs/versioned/next/reference/admin-config.md +7 -0
- package/src/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/src/docs/versioned/next/reference/admin-spa.md +18 -1
- package/src/docs/versioned/next/reference/ai.md +2 -0
- package/src/docs/versioned/next/reference/announcements.md +179 -0
- package/src/docs/versioned/next/reference/api.md +343 -4
- package/src/docs/versioned/next/reference/comms.md +33 -0
- package/src/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/src/docs/versioned/next/reference/environment-variables.md +29 -0
- package/src/docs/versioned/next/reference/jobs.md +319 -0
- package/src/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/src/docs/versioned/next/reference/mcp-server.md +2 -43
- package/src/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/src/docs/versioned/next/reference/syncdb.md +38 -12
- package/src/docs/versioned/next/reference/test.md +57 -6
- package/src/docs/versioned/next/reference/ui-performance.md +2 -1
- package/src/docs/versioned/next/reference/ui.md +257 -3
- package/src/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/src/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/src/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/src/docs/versioned/next/tasks/announcements.md +173 -0
- package/src/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/src/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/src/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/src/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/src/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/src/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/src/docs/versioned/next/tasks/job-queues.md +144 -0
- package/src/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/src/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/src/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/src/docs/versioned/next/tasks/notification-center.md +102 -0
- package/src/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/src/docs/versioned/next/tutorials/README.md +1 -1
- 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.
|