@terreno/mcp 57.0.0 → 57.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +381 -191
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
- package/dist/docs/resources/overview.md +6 -1
- package/dist/docs/resources/rtk.md +3 -1
- package/dist/docs/resources/syncdb.md +43 -0
- package/dist/docs/upgrades/56.0.0-beta.2.md +34 -0
- package/dist/docs/upgrades/57.1.0.md +126 -0
- package/dist/docs/versioned/next/README.md +15 -3
- package/dist/docs/versioned/next/explanation/README.md +3 -0
- package/dist/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
- package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
- package/dist/docs/versioned/next/explanation/authentication.md +51 -58
- package/dist/docs/versioned/next/explanation/local-first-data.md +52 -0
- package/dist/docs/versioned/next/explanation/loop-engineering.md +117 -0
- package/dist/docs/versioned/next/explanation/repository-settings.md +8 -0
- package/dist/docs/versioned/next/explanation/roadmap-process.md +22 -9
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
- package/dist/docs/versioned/next/how-to/README.md +3 -0
- package/dist/docs/versioned/next/how-to/admin-add-model.md +41 -0
- package/dist/docs/versioned/next/how-to/admin-custom-home.md +31 -0
- package/dist/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
- package/dist/docs/versioned/next/how-to/circleci.md +173 -0
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +13 -10
- package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +46 -0
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
- package/dist/docs/versioned/next/how-to/websocket-integration.md +2 -1
- package/dist/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/README.md +31 -17
- package/dist/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
- package/dist/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
- package/dist/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/admin-only.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
- package/dist/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
- package/dist/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
- package/dist/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
- package/dist/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
- package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
- package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
- package/dist/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
- package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
- package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
- package/dist/docs/versioned/next/reference/README.md +6 -1
- package/dist/docs/versioned/next/reference/admin-config.md +51 -0
- package/dist/docs/versioned/next/reference/ai.md +15 -1
- package/dist/docs/versioned/next/reference/api.md +18 -0
- package/dist/docs/versioned/next/reference/comms.md +88 -7
- package/dist/docs/versioned/next/reference/environment-variables.md +9 -1
- package/dist/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
- package/dist/docs/versioned/next/reference/mcp-server.md +7 -0
- package/dist/docs/versioned/next/reference/syncdb.md +30 -3
- package/dist/docs/versioned/next/tasks/README.md +21 -10
- package/dist/docs/versioned/next/tasks/admin-improvements.md +189 -353
- package/dist/docs/versioned/next/tasks/admin-only.md +2 -0
- package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
- package/dist/docs/versioned/next/tasks/comms-abstraction.md +30 -24
- package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
- package/dist/docs/versioned/next/tasks/consent-forms.md +2 -0
- package/dist/docs/versioned/next/tasks/feature-flags.md +2 -0
- package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
- package/dist/docs/versioned/next/tasks/model-router-actions.md +2 -0
- package/dist/docs/versioned/next/tasks/model-router-mcp.md +27 -27
- package/dist/docs/versioned/next/tasks/offline-mode.md +2 -0
- package/dist/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
- package/dist/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
- package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
- package/dist/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
- package/dist/docs/versioned/next/tasks/upgrade-banner.md +2 -0
- package/dist/index.js +24 -55
- package/dist/index.js.map +1 -1
- package/dist/local/localServer.d.ts.map +1 -1
- package/dist/local/localServer.js +9 -8
- package/dist/local/localServer.js.map +1 -1
- package/dist/local/localTools.d.ts +1 -1
- package/dist/local/localTools.d.ts.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +44 -45
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +9 -2
- package/dist/resources.js.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +4 -2
- package/dist/tools.js.map +1 -1
- package/package.json +7 -3
- package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
- package/src/docs/resources/overview.md +6 -1
- package/src/docs/resources/rtk.md +3 -1
- package/src/docs/resources/syncdb.md +43 -0
- package/src/docs/upgrades/56.0.0-beta.2.md +34 -0
- package/src/docs/upgrades/57.1.0.md +126 -0
- package/src/docs/versioned/next/README.md +15 -3
- package/src/docs/versioned/next/explanation/README.md +3 -0
- package/src/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
- package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
- package/src/docs/versioned/next/explanation/authentication.md +51 -58
- package/src/docs/versioned/next/explanation/local-first-data.md +52 -0
- package/src/docs/versioned/next/explanation/loop-engineering.md +117 -0
- package/src/docs/versioned/next/explanation/repository-settings.md +8 -0
- package/src/docs/versioned/next/explanation/roadmap-process.md +22 -9
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
- package/src/docs/versioned/next/how-to/README.md +3 -0
- package/src/docs/versioned/next/how-to/admin-add-model.md +41 -0
- package/src/docs/versioned/next/how-to/admin-custom-home.md +31 -0
- package/src/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
- package/src/docs/versioned/next/how-to/circleci.md +173 -0
- package/src/docs/versioned/next/how-to/configure-better-auth.md +13 -10
- package/src/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +46 -0
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
- package/src/docs/versioned/next/how-to/websocket-integration.md +2 -1
- package/src/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
- package/src/docs/versioned/next/implementationPlans/README.md +31 -17
- package/src/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
- package/src/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
- package/src/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
- package/src/docs/versioned/next/implementationPlans/admin-only.md +3 -1
- package/src/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
- package/src/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
- package/src/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
- package/src/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
- package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
- package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
- package/src/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
- package/src/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
- package/src/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
- package/src/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
- package/src/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
- package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
- package/src/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
- package/src/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
- package/src/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
- package/src/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
- package/src/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
- package/src/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
- package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
- package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
- package/src/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
- package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
- package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
- package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
- package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
- package/src/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
- package/src/docs/versioned/next/reference/README.md +6 -1
- package/src/docs/versioned/next/reference/admin-config.md +51 -0
- package/src/docs/versioned/next/reference/ai.md +15 -1
- package/src/docs/versioned/next/reference/api.md +18 -0
- package/src/docs/versioned/next/reference/comms.md +88 -7
- package/src/docs/versioned/next/reference/environment-variables.md +9 -1
- package/src/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
- package/src/docs/versioned/next/reference/mcp-server.md +7 -0
- package/src/docs/versioned/next/reference/syncdb.md +30 -3
- package/src/docs/versioned/next/tasks/README.md +21 -10
- package/src/docs/versioned/next/tasks/admin-improvements.md +189 -353
- package/src/docs/versioned/next/tasks/admin-only.md +2 -0
- package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
- package/src/docs/versioned/next/tasks/comms-abstraction.md +30 -24
- package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
- package/src/docs/versioned/next/tasks/consent-forms.md +2 -0
- package/src/docs/versioned/next/tasks/feature-flags.md +2 -0
- package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
- package/src/docs/versioned/next/tasks/model-router-actions.md +2 -0
- package/src/docs/versioned/next/tasks/model-router-mcp.md +27 -27
- package/src/docs/versioned/next/tasks/offline-mode.md +2 -0
- package/src/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
- package/src/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
- package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
- package/src/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
- package/src/docs/versioned/next/tasks/upgrade-banner.md +2 -0
- package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
- package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
- package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
- package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
# Implementation Plan: Pluggable communications layer (@terreno/comms)
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Approved — Phase 1 gap-fill implemented
|
|
4
4
|
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1018
|
|
5
5
|
**Priority:** High
|
|
6
6
|
**Effort:** Big batch
|
|
7
7
|
**Owner:** unassigned
|
|
8
8
|
**Created:** 2026-08-09
|
|
9
|
+
**Approved:** 2026-08-20
|
|
9
10
|
**Program:** [B2B platform](b2b-platform-program.md)
|
|
10
11
|
**Depends on:** none (inbound-webhooks is needed only by adapter delivery-callback phases)
|
|
11
12
|
**RTK deprecation flag:** None — backend-only package
|
|
13
|
+
**Shipped:** Phases 2–3 and most of Phase 1 in #1037; SendGrid adapter in #1050 (separate IP)
|
|
14
|
+
**Remaining:** none — Phase 1 gap-fill landed on this branch
|
|
15
|
+
**Research:** [comms-abstraction-research.md](comms-abstraction-research.md)
|
|
12
16
|
|
|
13
17
|
## Goal
|
|
14
18
|
|
|
@@ -26,14 +30,16 @@ consumer apps install only the SDKs they use.
|
|
|
26
30
|
|
|
27
31
|
## Non-Goals
|
|
28
32
|
|
|
29
|
-
- Any concrete provider SDK integration (see `comms-adapter-*` IPs).
|
|
33
|
+
- Any concrete provider SDK integration (see `comms-adapter-*` IPs). The SendGrid adapter
|
|
34
|
+
already lives at `@terreno/comms/adapters/sendgrid` under that IP; this IP does not own it.
|
|
30
35
|
- In-app notification center, inbox UI, or user notification preferences
|
|
31
36
|
(`notification-center` item).
|
|
32
37
|
- Password reset / email verification flows (`password-reset-and-email-verification` item
|
|
33
38
|
— a consumer of this layer).
|
|
34
|
-
- Inbound webhook handling (`inbound-webhooks` item); this IP
|
|
35
|
-
`
|
|
39
|
+
- Inbound webhook HTTP handling (`inbound-webhooks` item); this IP ships
|
|
40
|
+
`recordDeliveryEvent` / `recordOptOut` for adapters to call once those webhooks land.
|
|
36
41
|
- Broadcast/bulk campaign tooling.
|
|
42
|
+
- Admin retry UI and rich explorer filters ([comms-admin-dashboard](comms-admin-dashboard.md)).
|
|
37
43
|
|
|
38
44
|
## Decisions
|
|
39
45
|
|
|
@@ -48,6 +54,13 @@ consumer apps install only the SDKs they use.
|
|
|
48
54
|
| Consumer lifecycle hooks | `CommsApp` options expose `beforeSend` / `onSend` / `onError` / `onRetry` / `onOptOut` / `onDeliveryEvent`. Hooks are awaited but wrapped: a throwing hook is logged (`logger.error`) and never breaks the send. `beforeSend` may mutate or cancel |
|
|
49
55
|
| Error taxonomy | Every `SendResult` failure carries `errorCode` (provider-native code as a string) and `errorClass` (`"permanent"` \| `"transient"` \| `"config"`). Adapters own the mapping; the facade uses `errorClass` to decide its single inline retry (transient only) and the admin dashboard uses it to gate manual retries |
|
|
50
56
|
| Payload retention | `retainPayloadDays` option (default 30) keeps the rendered message on the `CommsMessage` row (post `redactPayload` hook) so the admin dashboard can retry; a TTL-style cleanup clears expired payloads |
|
|
57
|
+
| `onRetry` signature (published) | Keep shipped `(context, result: SendResult)`. Put 1-based `attempt` on `CommsHookContext` (2 = the inline retry). Do not change the callback to `(context, attempt)` |
|
|
58
|
+
| Provider throws | Facade never rethrows from `sendMail` / `sendSms` / `sendPushToUser` / `startVerification` / `checkVerification`. A throwing provider becomes `errorClass: "transient"`, `errorCode: "provider-throw"`, then follows the same retry rules |
|
|
59
|
+
| Push retry | Retry **only tokens** whose first `SendResult` is `errorClass: "transient"` via a second `sendPush`; merge. Do not re-send accepted tokens |
|
|
60
|
+
| Push prune | Deactivate when `errorClass === "permanent"` **or** `isPermanentFailure === true` (shipped alias; adapters may set both) |
|
|
61
|
+
| Delivery / opt-out intake | `CommsService.recordDeliveryEvent` / `recordOptOut` update the matching log row (by `providerMessageId`) and invoke hooks. Missing rows: `logger.warn`, still fire the hook |
|
|
62
|
+
| Payload expiry | `payloadExpiresAt` on the row. Mongo TTL indexes must **not** be used (they would delete the log). `CommsMessage.clearExpiredPayloads()` unsets `payload` only; `logSend` / `appendAttempt` call it best-effort (limit 50) |
|
|
63
|
+
| `checkVerification` | Log a verification row; no inline retry; never persist the code |
|
|
51
64
|
|
|
52
65
|
## Architecture
|
|
53
66
|
|
|
@@ -56,12 +69,12 @@ consumer apps install only the SDKs they use.
|
|
|
56
69
|
src/
|
|
57
70
|
types.ts # Provider interfaces + message/result types
|
|
58
71
|
commsApp.ts # CommsApp TerrenoPlugin
|
|
59
|
-
commsService.ts #
|
|
72
|
+
commsService.ts # send* + recordDeliveryEvent/recordOptOut
|
|
60
73
|
models/
|
|
61
74
|
commsMessage.ts # Delivery log
|
|
62
75
|
pushToken.ts # Device push token registry
|
|
63
76
|
routes/
|
|
64
|
-
pushTokens.ts #
|
|
77
|
+
pushTokens.ts # custom upsert/list/deactivate + owner read
|
|
65
78
|
commsExplorer.ts # Admin-only delivery log explorer
|
|
66
79
|
adapters/
|
|
67
80
|
console.ts # Dev adapters for all four channels
|
|
@@ -71,26 +84,39 @@ consumer apps install only the SDKs they use.
|
|
|
71
84
|
### Provider interfaces (types.ts)
|
|
72
85
|
|
|
73
86
|
```typescript
|
|
87
|
+
export type CommsErrorClass = "config" | "permanent" | "transient";
|
|
88
|
+
export type CommsChannel = "mail" | "push" | "sms" | "verification";
|
|
89
|
+
export type CommsMessageStatus =
|
|
90
|
+
| "bounced"
|
|
91
|
+
| "cancelled"
|
|
92
|
+
| "delivered"
|
|
93
|
+
| "failed"
|
|
94
|
+
| "sent";
|
|
95
|
+
|
|
74
96
|
export interface SendResult {
|
|
75
97
|
providerMessageId?: string;
|
|
76
98
|
accepted: boolean;
|
|
77
99
|
error?: string;
|
|
78
|
-
errorCode?: string;
|
|
79
|
-
errorClass?:
|
|
100
|
+
errorCode?: string;
|
|
101
|
+
errorClass?: CommsErrorClass;
|
|
102
|
+
isPermanentFailure?: boolean; // alias: treat as errorClass "permanent" when that is unset
|
|
103
|
+
metadata?: Record<string, unknown>;
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
export interface MailMessage {
|
|
83
107
|
to: string | string[];
|
|
84
|
-
from?: string;
|
|
108
|
+
from?: string;
|
|
85
109
|
subject: string;
|
|
86
110
|
text?: string;
|
|
87
111
|
html?: string;
|
|
88
112
|
replyTo?: string;
|
|
113
|
+
templateId?: string;
|
|
114
|
+
dynamicTemplateData?: Record<string, unknown>;
|
|
89
115
|
metadata?: Record<string, string>;
|
|
90
116
|
}
|
|
91
117
|
|
|
92
118
|
export interface MailProvider {
|
|
93
|
-
readonly id: string;
|
|
119
|
+
readonly id: string;
|
|
94
120
|
sendMail(message: MailMessage): Promise<SendResult>;
|
|
95
121
|
}
|
|
96
122
|
|
|
@@ -110,7 +136,7 @@ export interface PushMessage {
|
|
|
110
136
|
|
|
111
137
|
export interface PushProvider {
|
|
112
138
|
readonly id: string;
|
|
113
|
-
sendPush(message: PushMessage): Promise<SendResult[]>; // one per token
|
|
139
|
+
sendPush(message: PushMessage): Promise<SendResult[]>; // one per token, same order
|
|
114
140
|
}
|
|
115
141
|
|
|
116
142
|
export interface VerificationProvider {
|
|
@@ -126,29 +152,34 @@ export interface DeliveryEvent {
|
|
|
126
152
|
channel: "mail" | "sms" | "push";
|
|
127
153
|
providerMessageId: string;
|
|
128
154
|
status: "delivered" | "bounced" | "failed" | "opened";
|
|
129
|
-
errorCode?: string;
|
|
155
|
+
errorCode?: string;
|
|
156
|
+
errorClass?: CommsErrorClass;
|
|
130
157
|
raw?: unknown;
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
export interface CommsHookContext {
|
|
134
|
-
channel:
|
|
161
|
+
channel: CommsChannel;
|
|
135
162
|
provider: string;
|
|
136
|
-
message
|
|
163
|
+
message?: MailMessage | {to: string; body: string} | PushMessage | {
|
|
164
|
+
to: string;
|
|
165
|
+
channel: "sms" | "email";
|
|
166
|
+
};
|
|
137
167
|
userId?: string;
|
|
138
|
-
isRetry: boolean;
|
|
139
|
-
|
|
168
|
+
isRetry: boolean;
|
|
169
|
+
attempt: number; // 1 = first provider call, 2 = inline retry
|
|
170
|
+
messageId?: string; // CommsMessage._id once the log row exists
|
|
140
171
|
}
|
|
141
172
|
|
|
142
173
|
export interface OptOutEvent {
|
|
143
174
|
channel: "mail" | "sms";
|
|
144
175
|
to: string;
|
|
145
176
|
provider: string;
|
|
146
|
-
reason: string;
|
|
177
|
+
reason: string; // e.g. "sms-stop", "unsubscribe", "spam-report"
|
|
147
178
|
raw?: unknown;
|
|
148
179
|
}
|
|
149
180
|
```
|
|
150
181
|
|
|
151
|
-
Verification start attempts store `metadata.verificationChannel
|
|
182
|
+
Verification start attempts store `metadata.verificationChannel`. Recipients stay
|
|
152
183
|
redacted. Verification codes are never persisted.
|
|
153
184
|
|
|
154
185
|
### CommsApp (TerrenoPlugin)
|
|
@@ -159,18 +190,17 @@ export interface CommsOptions {
|
|
|
159
190
|
sms?: SmsProvider;
|
|
160
191
|
push?: PushProvider;
|
|
161
192
|
verification?: VerificationProvider;
|
|
162
|
-
defaultFrom?: string;
|
|
163
|
-
logMessages?: boolean; // default true
|
|
164
|
-
retainPayloadDays?: number; // default 30; 0
|
|
165
|
-
|
|
193
|
+
defaultFrom?: string;
|
|
194
|
+
logMessages?: boolean; // default true
|
|
195
|
+
retainPayloadDays?: number; // default 30; 0 stores no payload
|
|
196
|
+
redactRecipients?: boolean; // default true — `to` at rest
|
|
197
|
+
redactPayload?: (context: CommsHookContext, payload: unknown) => unknown;
|
|
166
198
|
|
|
167
|
-
// Lifecycle hooks — all optional, all awaited, all wrapped so a throwing
|
|
168
|
-
// hook is logged and never breaks (or blocks) the send itself.
|
|
169
199
|
beforeSend?: (context: CommsHookContext) =>
|
|
170
200
|
Promise<{cancel?: boolean; message?: CommsHookContext["message"]} | void>;
|
|
171
201
|
onSend?: (context: CommsHookContext, result: SendResult) => Promise<void>;
|
|
172
202
|
onError?: (context: CommsHookContext, result: SendResult) => Promise<void>;
|
|
173
|
-
onRetry?: (context: CommsHookContext,
|
|
203
|
+
onRetry?: (context: CommsHookContext, result: SendResult) => Promise<void>;
|
|
174
204
|
onOptOut?: (event: OptOutEvent) => Promise<void>;
|
|
175
205
|
onDeliveryEvent?: (event: DeliveryEvent) => Promise<void>;
|
|
176
206
|
}
|
|
@@ -181,10 +211,10 @@ new TerrenoApp({userModel: User})
|
|
|
181
211
|
```
|
|
182
212
|
|
|
183
213
|
`CommsApp.register()` mounts the push-token routes and admin explorer;
|
|
184
|
-
`getCommsService()` (module-level accessor
|
|
185
|
-
|
|
186
|
-
`
|
|
187
|
-
user's active `PushToken`s and prunes tokens the provider reports dead.
|
|
214
|
+
`getCommsService()` (module-level accessor) gives routes and other plugins `sendMail` /
|
|
215
|
+
`sendSms` / `sendPushToUser` / `startVerification` / `checkVerification` /
|
|
216
|
+
`recordDeliveryEvent` / `recordOptOut`. `sendPushToUser(userId, message)` resolves the
|
|
217
|
+
user's active `PushToken`s and prunes tokens the provider reports permanently dead.
|
|
188
218
|
|
|
189
219
|
Unconfigured channel behavior: in production, throw `APIError({status: 501, title:
|
|
190
220
|
"Comms channel not configured"})`; in development, fall back to the console adapter with a
|
|
@@ -192,22 +222,54 @@ Unconfigured channel behavior: in production, throw `APIError({status: 501, titl
|
|
|
192
222
|
|
|
193
223
|
### Hook order and semantics
|
|
194
224
|
|
|
195
|
-
For every send (including
|
|
225
|
+
For every send (including the inline retry) the facade runs:
|
|
196
226
|
|
|
197
|
-
1. `beforeSend` —
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
2. Provider `send*` → on
|
|
201
|
-
|
|
227
|
+
1. `beforeSend` — once, **before the first** provider call. May replace the message
|
|
228
|
+
(`{message}`) or cancel (`{cancel: true}` → `CommsMessage` with `status: "cancelled"`,
|
|
229
|
+
no provider call). Quiet hours, preferences, and suppression lists use this hook.
|
|
230
|
+
2. Provider `send*` → on `errorClass: "transient"` (or a wrapped provider throw),
|
|
231
|
+
`onRetry(context, failedResult)` fires with `context.attempt === 2` and `isRetry: true`,
|
|
232
|
+
then the facade retries once.
|
|
202
233
|
3. `onSend` (accepted) or `onError` (final failure) — `onError` receives the full
|
|
203
|
-
`SendResult
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
234
|
+
`SendResult`.
|
|
235
|
+
4. Later, adapters call `recordDeliveryEvent` / `recordOptOut`, which fire
|
|
236
|
+
`onDeliveryEvent` / `onOptOut`.
|
|
237
|
+
|
|
238
|
+
`checkVerification` skips `beforeSend` and inline retry. It still logs and fires
|
|
239
|
+
`onSend` / `onError`.
|
|
207
240
|
|
|
208
241
|
Hooks are observation and shaping points, not error channels: exceptions thrown inside a
|
|
209
|
-
hook are caught, logged with `logger.error`, and recorded in `CommsMessage.metadata`
|
|
210
|
-
they never change the send outcome.
|
|
242
|
+
hook are caught, logged with `logger.error`, and recorded in `CommsMessage.metadata.hookErrors`
|
|
243
|
+
— they never change the send outcome. A throwing `beforeSend` is treated as “no mutation,
|
|
244
|
+
do not cancel.”
|
|
245
|
+
|
|
246
|
+
### Attempt logging
|
|
247
|
+
|
|
248
|
+
One `CommsMessage` row per send invocation (not per retry):
|
|
249
|
+
|
|
250
|
+
1. After attempt 1, `logSend` creates the row (`attempts[0]`, `attemptCount: 1`).
|
|
251
|
+
2. After the inline retry, `appendAttempt` pushes `attempts[1]` and updates top-level
|
|
252
|
+
`status`, `error`, `errorCode`, `errorClass`, `providerMessageId`, `attemptCount`,
|
|
253
|
+
`lastAttemptAt`.
|
|
254
|
+
3. Cancelled sends create a row with `status: "cancelled"`, empty `attempts`, no provider.
|
|
255
|
+
|
|
256
|
+
`logSend` and `appendAttempt` never throw.
|
|
257
|
+
|
|
258
|
+
### Payload stored per channel
|
|
259
|
+
|
|
260
|
+
After `redactPayload` (identity if unset), when `retainPayloadDays > 0`:
|
|
261
|
+
|
|
262
|
+
| Channel | `payload` |
|
|
263
|
+
|---|---|
|
|
264
|
+
| mail | `{to, from, subject, text, html, replyTo, templateId, dynamicTemplateData}` |
|
|
265
|
+
| sms | `{to, body}` |
|
|
266
|
+
| push | `{title, body, data, badge, sound}` (tokens omitted) |
|
|
267
|
+
| verification start | `{channel}` only |
|
|
268
|
+
| verification check | none |
|
|
269
|
+
| cancelled | the mutated-or-original message, same shape as the channel |
|
|
270
|
+
|
|
271
|
+
`payloadExpiresAt = now + retainPayloadDays`. Expired payload is treated as absent for
|
|
272
|
+
retry even before cleanup runs.
|
|
211
273
|
|
|
212
274
|
## Models
|
|
213
275
|
|
|
@@ -216,7 +278,7 @@ and live per the five-type pattern (`CommsMessageDocument`, etc.).
|
|
|
216
278
|
|
|
217
279
|
**PushToken** — `userId` (ref User, required, indexed), `token` (string, required, unique),
|
|
218
280
|
`platform` ("ios" | "android" | "web"), `deviceId` (string, optional), `active` (boolean,
|
|
219
|
-
default true), `lastSeenAt` (date).
|
|
281
|
+
default true), `lastSeenAt` (date). Shipped.
|
|
220
282
|
|
|
221
283
|
**CommsMessage** — `channel` ("mail" | "sms" | "push" | "verification"), `provider`
|
|
222
284
|
(string), `to` (string, redacted-at-rest for sms/mail per option), `subject` (string,
|
|
@@ -224,11 +286,12 @@ optional), `userId` (ref User, optional), `status` ("sent" | "failed" | "deliver
|
|
|
224
286
|
"bounced" | "cancelled"), `providerMessageId` (string, indexed), `error` (string,
|
|
225
287
|
optional), `errorCode` (string, indexed), `errorClass` ("permanent" | "transient" |
|
|
226
288
|
"config"), `templateId` (string, optional), `attempts` (array of `{at, provider,
|
|
227
|
-
providerMessageId, errorCode, error}` — one entry per facade attempt including
|
|
228
|
-
retry), `attemptCount` (number), `lastAttemptAt` (date), `retriedFromId` /
|
|
229
|
-
(ref CommsMessage —
|
|
230
|
-
(Mixed, retained per `retainPayloadDays` after `redactPayload`, cleared on
|
|
231
|
-
`metadata` (Mixed).
|
|
289
|
+
providerMessageId, errorCode, errorClass, error}` — one entry per facade attempt including
|
|
290
|
+
the inline retry), `attemptCount` (number), `lastAttemptAt` (date), `retriedFromId` /
|
|
291
|
+
`retriedById` (ref CommsMessage — written later by admin-dashboard retries; schema exists
|
|
292
|
+
now), `payload` (Mixed, retained per `retainPayloadDays` after `redactPayload`, cleared on
|
|
293
|
+
expiry), `payloadExpiresAt` (date), `metadata` (Mixed). Statics: `logSend()`,
|
|
294
|
+
`appendAttempt()`, `clearExpiredPayloads()`.
|
|
232
295
|
|
|
233
296
|
These fields exist so the [comms-admin-dashboard](comms-admin-dashboard.md) IP can filter
|
|
234
297
|
on error codes/classes, show per-attempt history, and re-send from the retained payload
|
|
@@ -250,6 +313,8 @@ The explorer uses `createOpenApiBuilder`, modeled on `addAiRequestsExplorerRoute
|
|
|
250
313
|
deliberately minimal here — the [comms-admin-dashboard](comms-admin-dashboard.md) IP upgrades it
|
|
251
314
|
with error-code/class filtering, free-text search, a detail route, stats, and retry endpoints.
|
|
252
315
|
|
|
316
|
+
No HTTP routes for `recordDeliveryEvent` / `recordOptOut` in this IP.
|
|
317
|
+
|
|
253
318
|
## Notifications
|
|
254
319
|
|
|
255
320
|
None beyond the feature itself.
|
|
@@ -263,14 +328,18 @@ None in this IP. example-frontend gains a settings row that registers the Expo p
|
|
|
263
328
|
|
|
264
329
|
1. **Package + contracts:** `@terreno/comms` package scaffold, `types.ts`, console
|
|
265
330
|
adapters, `CommsMessage` + logging, `commsService`, unit tests.
|
|
331
|
+
**Shipped** including dashboard-ready fields, hooks, payload retention, and
|
|
332
|
+
channel-wide transient retry.
|
|
266
333
|
2. **CommsApp + routes:** plugin registration, `PushToken` model + routes, admin explorer,
|
|
267
|
-
OpenAPI, supertest coverage.
|
|
334
|
+
OpenAPI, supertest coverage. **Shipped.**
|
|
268
335
|
3. **Integration:** example-backend registers `CommsApp` (console adapters), seed docs,
|
|
269
336
|
`docs/reference/comms.md`, catalog entries, publish wiring in `publish-on-tag.yml`.
|
|
337
|
+
**Shipped.**
|
|
270
338
|
|
|
271
339
|
## Feature Flags & Migrations
|
|
272
340
|
|
|
273
|
-
None. New collections only; no existing data touched.
|
|
341
|
+
None. New collections only; no existing data touched. Remaining schema fields are additive
|
|
342
|
+
on `CommsMessage` (`cancelled` status value, `attempts`, payload fields, retry refs).
|
|
274
343
|
|
|
275
344
|
## Activity Log & User Updates
|
|
276
345
|
|
|
@@ -280,7 +349,8 @@ updates.
|
|
|
280
349
|
## Not Included / Future Work
|
|
281
350
|
|
|
282
351
|
- Adapter IPs: `comms-adapter-expo-push`, `comms-adapter-twilio-sms`,
|
|
283
|
-
`comms-adapter-twilio-verify`, `comms-adapter-sendgrid
|
|
352
|
+
`comms-adapter-twilio-verify`, `comms-adapter-sendgrid` (Phase 1 of SendGrid already
|
|
353
|
+
shipped).
|
|
284
354
|
- Admin operations dashboard (rich filtering, log digging, manual retries) —
|
|
285
355
|
[comms-admin-dashboard](comms-admin-dashboard.md); this IP only ships the fields and
|
|
286
356
|
hooks it needs.
|
|
@@ -288,15 +358,20 @@ updates.
|
|
|
288
358
|
point they will plug into).
|
|
289
359
|
- Queue-backed sending with automatic scheduled retries — arrives with `job-queues`;
|
|
290
360
|
until then sends are inline with one transient-error retry plus manual admin retries.
|
|
361
|
+
- Webhook HTTP for delivery callbacks — `inbound-webhooks` plus adapter Phase 2.
|
|
291
362
|
|
|
292
363
|
## Files to Create / Modify
|
|
293
364
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
- `docs/reference/comms.md`, `.
|
|
299
|
-
|
|
365
|
+
Remaining roast (gap-fill):
|
|
366
|
+
|
|
367
|
+
- `comms/src/types.ts`, `commsService.ts`, `models/commsMessage.ts`, `modelTypes.ts`
|
|
368
|
+
- Tests for hooks, retry-per-channel, attempts, payload expiry
|
|
369
|
+
- `docs/reference/comms.md`, `.rulesync/rules/comms/00-comms.md` (then `bun run rules`)
|
|
370
|
+
|
|
371
|
+
Already shipped:
|
|
372
|
+
|
|
373
|
+
- `comms/` package scaffold, console adapters, `CommsApp`, routes, CI/publish
|
|
374
|
+
- Root `package.json` scripts, example-backend registration
|
|
300
375
|
|
|
301
376
|
## Task List
|
|
302
377
|
|
|
@@ -307,12 +382,15 @@ See [docs/tasks/comms-abstraction.md](../tasks/comms-abstraction.md).
|
|
|
307
382
|
- [x] A Terreno app with only console adapters can call `sendMail`, `sendSms`,
|
|
308
383
|
`sendPushToUser`, and `startVerification`/`checkVerification` in development, each
|
|
309
384
|
producing a logger line and a `CommsMessage` row.
|
|
310
|
-
- [
|
|
311
|
-
`SendResult`; `onRetry` fires on the inline transient retry
|
|
312
|
-
is logged without changing the send
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
- [
|
|
385
|
+
- [x] `beforeSend` can cancel and mutate; `onSend`/`onError` fire with the final
|
|
386
|
+
`SendResult`; `onRetry` fires on the inline transient retry with
|
|
387
|
+
`context.attempt === 2` — and a hook that throws is logged without changing the send
|
|
388
|
+
outcome (test per hook, including `onOptOut` / `onDeliveryEvent` via the record
|
|
389
|
+
methods).
|
|
390
|
+
- [x] Failed sends record `errorCode` and `errorClass` on both the `SendResult` and the
|
|
391
|
+
`CommsMessage` row; only `transient` failures trigger the inline retry; provider
|
|
392
|
+
throws become transient and do not propagate.
|
|
393
|
+
- [x] `CommsMessage.attempts` holds one entry per attempt with per-attempt error data;
|
|
316
394
|
`payload` is retained post-redaction and cleared after `retainPayloadDays`.
|
|
317
395
|
- [x] `POST /comms/pushTokens` registers a device token for the authenticated user;
|
|
318
396
|
re-posting the same token updates rather than duplicates; other users cannot list or
|
|
@@ -321,3 +399,15 @@ See [docs/tasks/comms-abstraction.md](../tasks/comms-abstraction.md).
|
|
|
321
399
|
- [x] Production apps with an unconfigured channel get a 501 `APIError`, not a silent no-op.
|
|
322
400
|
- [x] `@terreno/comms` core has zero provider SDKs in `dependencies`.
|
|
323
401
|
- [x] All routes appear in `/openapi.json` and the generated SDK compiles.
|
|
402
|
+
- [x] Push retries re-send only transient-failed tokens; permanent failures deactivate
|
|
403
|
+
tokens via `errorClass` or `isPermanentFailure`.
|
|
404
|
+
- [x] `recordDeliveryEvent` updates status by `providerMessageId`; `recordOptOut` fires
|
|
405
|
+
`onOptOut` without sending.
|
|
406
|
+
|
|
407
|
+
## Named assumptions (finish pass)
|
|
408
|
+
|
|
409
|
+
1. Original Decisions table stands; remaining work implements the IP rather than shrinking
|
|
410
|
+
it to today’s partial code.
|
|
411
|
+
2. Published `onRetry(context, result)` stays; `attempt` is added on context.
|
|
412
|
+
3. Roadmap issue #1018 already exists; Blend does not open a second issue. Maintainer can
|
|
413
|
+
move Project status when roast of remaining tasks starts.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: Comms adapter — transactional email (SendGrid)
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** In progress — Phase 1 send path implemented; Phase 2 gated on inbound-webhooks
|
|
4
4
|
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1022
|
|
5
5
|
**Priority:** High
|
|
6
6
|
**Effort:** Small batch
|
|
@@ -135,22 +135,22 @@ See [docs/tasks/comms-adapter-sendgrid.md](../tasks/comms-adapter-sendgrid.md).
|
|
|
135
135
|
|
|
136
136
|
## Acceptance Criteria
|
|
137
137
|
|
|
138
|
-
- [
|
|
138
|
+
- [x] `sendMail` delivers text/html mail with from-address fallback chain (provider option
|
|
139
139
|
→ `CommsApp.defaultFrom`), and passes `templateId`/`dynamicTemplateData` through when
|
|
140
140
|
given.
|
|
141
|
-
- [
|
|
141
|
+
- [x] Sandbox mode is on by default under test and produces accepted results without real
|
|
142
142
|
sends.
|
|
143
|
-
- [
|
|
143
|
+
- [x] SendGrid errors surface as `accepted: false` with the SendGrid error message and a
|
|
144
144
|
correct `errorCode`/`errorClass` per the classification table; nothing throws
|
|
145
145
|
through the facade.
|
|
146
|
-
- [
|
|
146
|
+
- [x] 401/403 classify as `config` and `logger.error` at send time; 429/5xx classify as
|
|
147
147
|
`transient` and retry once; other 4xx are `permanent` and never retried.
|
|
148
|
-
- [
|
|
148
|
+
- [x] Each accepted send captures the `x-message-id` and stores a `metadata.consoleUrl`
|
|
149
149
|
Email Activity deep link.
|
|
150
|
-
- [
|
|
150
|
+
- [x] Consumer `onError` fires with the classified `SendResult` on every failed send
|
|
151
151
|
(mocked-client test).
|
|
152
|
-
- [
|
|
153
|
-
- [
|
|
152
|
+
- [x] Missing API key fails fast at `CommsApp` registration with a clear error.
|
|
153
|
+
- [x] Apps not using the adapter do not install `@sendgrid/mail`.
|
|
154
154
|
- [ ] (Phase 2) Event webhook updates `CommsMessage.status` per the mapping table only
|
|
155
155
|
for signature-verified payloads; bounce/dropped reasons land as `errorCode`.
|
|
156
156
|
- [ ] (Phase 2) `spamreport`/`unsubscribe` events fire `onOptOut`; later sends to the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Implementation Plan: Comms admin dashboard (errors, retries, log digging)
|
|
2
2
|
|
|
3
3
|
**Status:** Draft
|
|
4
|
-
**Roadmap issue:**
|
|
4
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1091
|
|
5
5
|
**Priority:** High
|
|
6
6
|
**Effort:** Medium batch
|
|
7
7
|
**Owner:** unassigned
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Implementation Plan: Consent Forms System
|
|
2
2
|
|
|
3
|
+
**Status:** Complete
|
|
4
|
+
**Closed:** 2026-08-20 — Shipped via `ConsentApp`, `ConsentForm`/`ConsentResponse` models, and `@terreno/ui` consent screens (`ConsentNavigator`, `ConsentFormScreen`, `useConsentForms`).
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1078
|
|
6
|
+
|
|
3
7
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
8
|
|
|
5
9
|
## **Models**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: design-blend Skill
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1083
|
|
3
4
|
## Goal
|
|
4
5
|
|
|
5
6
|
Create a new `design-blend` skill that converts a Claude design file into a Terreno implementation blueprint and execution steps, with these hard constraints:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: Documentation Site, Versioned Docs, and Docs-Maintenance Skills
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1086
|
|
3
4
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
5
|
|
|
5
6
|
## Overview
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: OpenFeature Adoption for `@terreno/feature-flags`
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1082
|
|
3
4
|
## Goal
|
|
4
5
|
|
|
5
6
|
Migrate `@terreno/feature-flags` (backend) and `@terreno/rtk`'s `useFeatureFlags` (frontend) to be built on top of the OpenFeature SDKs (`@openfeature/server-sdk`, `@openfeature/react-sdk`) while preserving:
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Implementation Plan: Feature Flags & A/B Testing
|
|
2
2
|
|
|
3
|
+
**Status:** Deferred
|
|
4
|
+
**Closed:** 2026-08-20 — Superseded by [feature-flags-openfeature.md](./feature-flags-openfeature.md) (OpenFeature provider, live updates). This document is retained for historical context only.
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1098
|
|
6
|
+
|
|
3
7
|
> **OpenFeature migration:** For the OpenFeature-based provider, `/flagConfiguration`, live updates, and RTK hook details, see [feature-flags-openfeature.md](./feature-flags-openfeature.md) and the task list [feature-flags-openfeature.md](../tasks/feature-flags-openfeature.md).
|
|
4
8
|
|
|
5
9
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Research: Infrastructure MCP server (privileged tools gateway)
|
|
2
2
|
|
|
3
3
|
**Status:** Complete — decisions made, IP written: [infra-mcp.md](infra-mcp.md)
|
|
4
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1090
|
|
4
5
|
**Created:** 2026-08-09
|
|
5
6
|
|
|
6
7
|
## Scope statement
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Implementation Plan: Infrastructure MCP server (`@terreno/infra-mcp`)
|
|
2
2
|
|
|
3
|
-
**Status:** Draft —
|
|
3
|
+
**Status:** Draft — unblocked; RBAC module is Complete ([rbac-permissions.md](rbac-permissions.md))
|
|
4
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1090
|
|
4
5
|
**Branch:** TBD
|
|
5
6
|
**Owner:** TBD
|
|
6
7
|
**Created:** 2026-08-09
|
|
@@ -228,7 +229,7 @@ No new frontend package. Admin surfaces reuse `@terreno/admin-backend` + `@terre
|
|
|
228
229
|
|
|
229
230
|
- **No data migrations** — all collections are new.
|
|
230
231
|
- Rollout is additive: new package, new Cloud Run service. Nothing existing changes until Phase 2's cloud-agent credential switch, which is coordinated: keep `GCP_SA_*` injection until the MCP path is verified, then remove.
|
|
231
|
-
-
|
|
232
|
+
- RBAC dependency: the RBAC module is Complete ([rbac-permissions.md](rbac-permissions.md)). Phase 1 wiring consumes `createAccess`, `rbacUserPlugin`, `rbacRouter`, default-role seeding.
|
|
232
233
|
|
|
233
234
|
## Activity Log & User Updates
|
|
234
235
|
|
|
@@ -249,7 +250,6 @@ No new frontend package. Admin surfaces reuse `@terreno/admin-backend` + `@terre
|
|
|
249
250
|
- `package.json` (root) — workspace entry, catalog: zod 4, `@modelcontextprotocol/server`, `@better-auth/oauth-provider`, `@better-auth/mcp`, Google API clients.
|
|
250
251
|
- `terraform/main.tf`, `variables.tf`, `outputs.tf` — `infra_mcp_service` module instance + runtime SA IAM.
|
|
251
252
|
- `.cursor/rules/infra-mcp/00-infra-mcp.mdc`, `AGENTS.md`/`CLAUDE.md` package lists, docs site page.
|
|
252
|
-
- `docs/implementationPlans/PLAN_INDEX.md` — register this IP.
|
|
253
253
|
- `ROADMAP.md` + `docs/explanation/roadmap-seed-issues.md` — roadmap entry and ready-to-paste tracking issue (done); `scripts/issueAreaLabels.ts` + bug-report package dropdown when the package lands.
|
|
254
254
|
|
|
255
255
|
## Task List
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: MCP Server — Boost Parity (Docs Search, Local Runtime Tools, Per-Package Guidelines, Upgrade Prompts)
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1085
|
|
3
4
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
5
|
|
|
5
6
|
## Overview
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Research: Migrate CI/CD to CircleCI
|
|
2
2
|
|
|
3
3
|
**Status:** Research complete — decisions recorded in IP `migrate-cicd-to-circleci.md`
|
|
4
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1088
|
|
4
5
|
**Date:** 2026-08-17
|
|
5
6
|
**Repo:** FlourishHealth/terreno (`master` @ `1e3e79a6`)
|
|
6
7
|
**Branch:** `cursor/migrate-cicd-circleci-2b6d`
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Implementation Plan: Migrate CI/CD to CircleCI
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** In progress — Phase 1–3 CI config landed (deploys deferred per request)
|
|
4
4
|
**Discussion:** _(none)_
|
|
5
|
-
**Roadmap issue:**
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1088
|
|
6
6
|
**Linear:** _(none)_
|
|
7
7
|
**Priority:** High
|
|
8
8
|
**Effort:** Large (cross-cutting infra; many workflows)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Implementation Plan: modelRouter Actions
|
|
2
2
|
|
|
3
|
+
**Status:** Deferred
|
|
4
|
+
**Closed:** 2026-08-20 — Not pursued; custom `endpoints` callbacks remain the supported pattern for non-CRUD routes.
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1097
|
|
6
|
+
|
|
3
7
|
## Goal
|
|
4
8
|
|
|
5
9
|
Add first-class support for **actions** in `modelRouter` — URLs that perform a named operation rather than CRUD. Actions live either at `/resource/action` (collection scope) or `/resource/:id/action` (instance scope). They inherit modelRouter's permissions system, are wrapped in `asyncHandler` + the standard error envelope automatically, and appear in the generated OpenAPI spec without extra wiring.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Implementation Plan: modelRouter MCP Tools
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1092
|
|
3
4
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
5
|
|
|
5
6
|
## Overview
|
|
@@ -17,7 +18,8 @@ This makes every Terreno app instantly AI-native: any model becomes callable by
|
|
|
17
18
|
- Auth-agnostic: works with both JWT and Better Auth
|
|
18
19
|
- Tool naming: `{prefix}_list`, `{prefix}_read`, `{prefix}_create`, `{prefix}_update`, `{prefix}_delete`
|
|
19
20
|
- Populate is an option on the read tool, not a separate tool
|
|
20
|
-
- Also exports `getMCPTools()` returning Vercel `ai` SDK `CoreTool` objects for direct in-process use
|
|
21
|
+
- Also exports `getMCPTools()` from `@terreno/ai` returning Vercel `ai` SDK `CoreTool` objects for direct in-process use
|
|
22
|
+
- Custom tools via `registerMCPTool` (example: `users_todo_statuses`)
|
|
21
23
|
|
|
22
24
|
**Related work:** The `ai-multimodal-tools-mcp` branch has `MCPService` (MCP client) and `AIService` in `@terreno/ai` — that's the consumer of what this plan builds.
|
|
23
25
|
|
|
@@ -112,7 +114,7 @@ Generated from model name, field names/types, and `queryFields`. Example:
|
|
|
112
114
|
|
|
113
115
|
### CoreTool Export
|
|
114
116
|
|
|
115
|
-
`getMCPTools()` exported from `@terreno/
|
|
117
|
+
`getMCPTools()` exported from `@terreno/ai` returns all registered MCP tools as `Record<string, CoreTool>` for direct use with the Vercel `ai` SDK's `streamText()` / `generateText()`.
|
|
116
118
|
|
|
117
119
|
## Notifications
|
|
118
120
|
|
|
@@ -175,8 +177,7 @@ No activity logging specific to this feature. MCP tool calls go through the same
|
|
|
175
177
|
## Not Included / Future Work
|
|
176
178
|
|
|
177
179
|
- **Rate limiting** — structured for future hooks but not implemented
|
|
178
|
-
- **MCP resources/prompts** — only tools generated from modelRouter
|
|
179
|
-
- **Custom MCP tools** — use modelRouter's `endpoints` for custom routes; MCP-specific custom tools deferred
|
|
180
|
+
- **MCP resources/prompts** — only tools generated from modelRouter plus `registerMCPTool`
|
|
180
181
|
- **Array operation tools** — handled via update, no dedicated push/patch/delete array tools
|
|
181
182
|
- **WebSocket transport** — HTTP/SSE only
|
|
182
183
|
- **Admin dashboard** — use `getMCPTools()` programmatically or MCP client tool listing
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Implementation Plan: Offline Mode
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Deferred
|
|
4
|
+
**Closed:** 2026-08-20 — Superseded by [@terreno/syncdb](syncdb-local-first.md) local-first outbox and offline-capable sync.
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1096
|
|
4
6
|
**Priority:** High
|
|
5
7
|
**Effort:** Medium
|
|
6
8
|
|