@terreno/mcp 56.0.0-beta.0 → 57.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/bootstrap.js +10 -11
  4. package/dist/bootstrap.js.map +1 -1
  5. package/dist/docs/templates/bootstrap/backend/types/models/userTypes.template.md +5 -4
  6. package/dist/docs/templates/tools/model.template.md +4 -2
  7. package/dist/docs/upgrades/0.31.0.md +60 -0
  8. package/dist/docs/upgrades/56.0.0-beta.2.md +59 -0
  9. package/dist/docs/upgrades/57.0.0-beta.1.md +41 -0
  10. package/dist/docs/versioned/next/README.md +1 -0
  11. package/dist/docs/versioned/next/explanation/README.md +7 -1
  12. package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +65 -0
  13. package/dist/docs/versioned/next/explanation/deployment-baseline.md +78 -0
  14. package/dist/docs/versioned/next/explanation/positioning.md +164 -0
  15. package/dist/docs/versioned/next/explanation/repository-settings.md +44 -0
  16. package/dist/docs/versioned/next/explanation/roadmap-process.md +330 -0
  17. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +887 -0
  18. package/dist/docs/versioned/next/how-to/README.md +8 -0
  19. package/dist/docs/versioned/next/how-to/build-for-web.md +69 -0
  20. package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +115 -0
  21. package/dist/docs/versioned/next/how-to/deploy-to-gcp.md +20 -228
  22. package/dist/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +183 -0
  23. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +31 -0
  24. package/dist/docs/versioned/next/implementationPlans/README.md +56 -30
  25. package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +2 -1
  26. package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +1 -0
  27. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +115 -0
  28. package/dist/docs/versioned/next/implementationPlans/billing-stripe.md +158 -0
  29. package/dist/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +1 -0
  30. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +323 -0
  31. package/dist/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +107 -0
  32. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +157 -0
  33. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +155 -0
  34. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +99 -0
  35. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  36. package/dist/docs/versioned/next/implementationPlans/deploy-to-gcp.md +1 -0
  37. package/dist/docs/versioned/next/implementationPlans/deploy-to-vercel.md +1 -0
  38. package/dist/docs/versioned/next/implementationPlans/deployment-foundation.md +1 -0
  39. package/dist/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -0
  40. package/dist/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +1 -0
  41. package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -0
  42. package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +188 -0
  43. package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +269 -0
  44. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  45. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  46. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +105 -0
  47. package/dist/docs/versioned/next/implementationPlans/orgs-and-teams.md +158 -0
  48. package/dist/docs/versioned/next/implementationPlans/oss-governance-baseline.md +1 -0
  49. package/dist/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +147 -0
  50. package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -0
  51. package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +1 -0
  52. package/dist/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -0
  53. package/dist/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +1 -0
  54. package/dist/docs/versioned/next/reference/README.md +5 -0
  55. package/dist/docs/versioned/next/reference/admin-spa.md +130 -0
  56. package/dist/docs/versioned/next/reference/ai.md +519 -0
  57. package/dist/docs/versioned/next/reference/comms.md +132 -0
  58. package/dist/docs/versioned/next/reference/environment-variables.md +229 -382
  59. package/dist/docs/versioned/next/reference/test.md +108 -0
  60. package/dist/docs/versioned/next/roadmap.md +9 -0
  61. package/dist/docs/versioned/next/tasks/README.md +42 -0
  62. package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  63. package/dist/docs/versioned/next/tasks/billing-stripe.md +75 -0
  64. package/dist/docs/versioned/next/tasks/comms-abstraction.md +90 -0
  65. package/dist/docs/versioned/next/tasks/comms-adapter-expo-push.md +29 -0
  66. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +39 -0
  67. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +39 -0
  68. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +19 -0
  69. package/dist/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  70. package/dist/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  71. package/dist/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  72. package/dist/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  73. package/dist/docs/versioned/next/tasks/infra-mcp.md +43 -0
  74. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  75. package/dist/docs/versioned/next/tasks/native-module-baseline.md +42 -0
  76. package/dist/docs/versioned/next/tasks/orgs-and-teams.md +70 -0
  77. package/dist/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  78. package/dist/docs/versioned/next/tasks/password-reset-and-email-verification.md +60 -0
  79. package/dist/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  80. package/dist/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
  81. package/package.json +4 -3
  82. package/src/docs/templates/bootstrap/backend/types/models/userTypes.template.md +5 -4
  83. package/src/docs/templates/tools/model.template.md +4 -2
  84. package/src/docs/upgrades/0.31.0.md +60 -0
  85. package/src/docs/upgrades/56.0.0-beta.2.md +59 -0
  86. package/src/docs/upgrades/57.0.0-beta.1.md +41 -0
  87. package/src/docs/versioned/next/README.md +1 -0
  88. package/src/docs/versioned/next/explanation/README.md +7 -1
  89. package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +65 -0
  90. package/src/docs/versioned/next/explanation/deployment-baseline.md +78 -0
  91. package/src/docs/versioned/next/explanation/positioning.md +164 -0
  92. package/src/docs/versioned/next/explanation/repository-settings.md +44 -0
  93. package/src/docs/versioned/next/explanation/roadmap-process.md +330 -0
  94. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +887 -0
  95. package/src/docs/versioned/next/how-to/README.md +8 -0
  96. package/src/docs/versioned/next/how-to/build-for-web.md +69 -0
  97. package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +115 -0
  98. package/src/docs/versioned/next/how-to/deploy-to-gcp.md +20 -228
  99. package/src/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +183 -0
  100. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +31 -0
  101. package/src/docs/versioned/next/implementationPlans/README.md +56 -30
  102. package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +2 -1
  103. package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +1 -0
  104. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +115 -0
  105. package/src/docs/versioned/next/implementationPlans/billing-stripe.md +158 -0
  106. package/src/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +1 -0
  107. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +323 -0
  108. package/src/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +107 -0
  109. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +157 -0
  110. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +155 -0
  111. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +99 -0
  112. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  113. package/src/docs/versioned/next/implementationPlans/deploy-to-gcp.md +1 -0
  114. package/src/docs/versioned/next/implementationPlans/deploy-to-vercel.md +1 -0
  115. package/src/docs/versioned/next/implementationPlans/deployment-foundation.md +1 -0
  116. package/src/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -0
  117. package/src/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +1 -0
  118. package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -0
  119. package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +188 -0
  120. package/src/docs/versioned/next/implementationPlans/infra-mcp.md +269 -0
  121. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  122. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  123. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +105 -0
  124. package/src/docs/versioned/next/implementationPlans/orgs-and-teams.md +158 -0
  125. package/src/docs/versioned/next/implementationPlans/oss-governance-baseline.md +1 -0
  126. package/src/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +147 -0
  127. package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -0
  128. package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +1 -0
  129. package/src/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -0
  130. package/src/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +1 -0
  131. package/src/docs/versioned/next/reference/README.md +5 -0
  132. package/src/docs/versioned/next/reference/admin-spa.md +130 -0
  133. package/src/docs/versioned/next/reference/ai.md +519 -0
  134. package/src/docs/versioned/next/reference/comms.md +132 -0
  135. package/src/docs/versioned/next/reference/environment-variables.md +229 -382
  136. package/src/docs/versioned/next/reference/test.md +108 -0
  137. package/src/docs/versioned/next/roadmap.md +9 -0
  138. package/src/docs/versioned/next/tasks/README.md +42 -0
  139. package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  140. package/src/docs/versioned/next/tasks/billing-stripe.md +75 -0
  141. package/src/docs/versioned/next/tasks/comms-abstraction.md +90 -0
  142. package/src/docs/versioned/next/tasks/comms-adapter-expo-push.md +29 -0
  143. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +39 -0
  144. package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +39 -0
  145. package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +19 -0
  146. package/src/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  147. package/src/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  148. package/src/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  149. package/src/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  150. package/src/docs/versioned/next/tasks/infra-mcp.md +43 -0
  151. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  152. package/src/docs/versioned/next/tasks/native-module-baseline.md +42 -0
  153. package/src/docs/versioned/next/tasks/orgs-and-teams.md +70 -0
  154. package/src/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  155. package/src/docs/versioned/next/tasks/password-reset-and-email-verification.md +60 -0
  156. package/src/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  157. package/src/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
@@ -0,0 +1,155 @@
1
+ # Implementation Plan: Comms adapter — Twilio SMS
2
+
3
+ **Status:** Draft
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1020
5
+ **Priority:** Medium
6
+ **Effort:** Small batch
7
+ **Owner:** unassigned
8
+ **Created:** 2026-08-09
9
+ **Program:** [B2B platform](b2b-platform-program.md)
10
+ **Depends on:** [comms-abstraction](comms-abstraction.md); inbound-webhooks (Phase 2 only)
11
+ **RTK deprecation flag:** None — backend only
12
+
13
+ ## Goal
14
+
15
+ Implement `SmsProvider` on Twilio Programmable Messaging so Terreno apps can send
16
+ transactional SMS (OTP fallback, alerts, invitation nudges) through the `@terreno/comms`
17
+ facade with delivery logging.
18
+
19
+ ## Non-Goals
20
+
21
+ - OTP verification (that is `comms-adapter-twilio-verify`, on Twilio Verify).
22
+ - Push via Twilio (dropped by decision D5 — Expo push is the push adapter).
23
+ - Bulk/campaign messaging, MMS/RCS.
24
+ - Phone-number provisioning automation.
25
+
26
+ ## Decisions
27
+
28
+ | Question | Decision |
29
+ |----------|----------|
30
+ | SDK | `twilio` as optional peer, subpath `@terreno/comms/adapters/twilioSms` |
31
+ | Sender config | Prefer `TWILIO_MESSAGING_SERVICE_SID`; fall back to `TWILIO_FROM_NUMBER`; constructor options override env |
32
+ | Number validation | Validate/normalize to E.164 with `libphonenumber-js` (already in catalog) before hitting the API |
33
+ | Delivery status | Phase 2: status callback endpoint via the inbound-webhooks framework mapping Twilio statuses to `DeliveryEvent`s |
34
+ | Secrets | `TWILIO_ACCOUNT_SID` + `TWILIO_AUTH_TOKEN` via env or the existing secret-provider layer |
35
+ | Error classification | Adapter owns a Twilio error-code → `errorClass` map (table below) so the facade retries only transient failures and the admin dashboard gates manual retries correctly |
36
+ | SDK failure handling | Twilio SDK calls wrapped with `withApiErrorHandling` from `@terreno/api` (per [call-external-apis](../how-to/call-external-apis.md)) — the SDK owns transport, the wrapper standardizes logging/normalization |
37
+ | Opt-outs | Phase 2: inbound STOP/START (and 21610 send failures) surface as `OptOutEvent` → the consumer's `onOptOut` hook; enforcement stays consumer-side via `beforeSend` (Twilio blocks opted-out sends itself) |
38
+ | Admin dashboard support | Every `CommsMessage` row stores the Twilio error code/class and a `metadata.consoleUrl` deep link to the message in the Twilio console for log digging |
39
+
40
+ ## Architecture
41
+
42
+ ```typescript
43
+ // @terreno/comms/adapters/twilioSms
44
+ export class TwilioSmsProvider implements SmsProvider {
45
+ readonly id = "twilio";
46
+ constructor(options?: {
47
+ accountSid?: string; // default env TWILIO_ACCOUNT_SID
48
+ authToken?: string; // default env TWILIO_AUTH_TOKEN
49
+ messagingServiceSid?: string;
50
+ fromNumber?: string;
51
+ statusCallbackUrl?: string; // Phase 2
52
+ });
53
+ sendSms(message: {to: string; body: string}): Promise<SendResult>;
54
+ }
55
+ ```
56
+
57
+ Errors map Twilio exception codes to `SendResult.error`/`errorCode`/`errorClass`, with
58
+ the full Twilio error payload kept in `CommsMessage.metadata` for triage. Invalid
59
+ destination numbers fail fast with a `BadRequestError` before any API call.
60
+
61
+ ### Error classification
62
+
63
+ The adapter classifies Twilio error codes into the abstraction's `errorClass` taxonomy.
64
+ Permanent failures must never be auto-retried (retrying an unsubscribed number is a
65
+ compliance problem, not a delivery problem); transient failures are retried once inline
66
+ by the facade and stay manually retryable from the admin dashboard.
67
+
68
+ | Twilio code(s) | Meaning | `errorClass` |
69
+ |---|---|---|
70
+ | 21211, 21214, 21217 | Invalid / unroutable destination number | `permanent` |
71
+ | 21408 | SMS not enabled for destination region | `permanent` |
72
+ | 21610 | Recipient has opted out (STOP) | `permanent` (also emits `OptOutEvent`) |
73
+ | 30003, 30005, 30006 | Unreachable handset / unknown device / landline | `permanent` |
74
+ | 30007 | Carrier content filtering | `permanent` (surface loudly — usually a sender-reputation problem) |
75
+ | 20429, 429 responses | Rate limited / concurrency limit | `transient` |
76
+ | 30001, 30002 | Queue overflow / account suspended-ish delivery interruption | `transient` |
77
+ | 5xx / network errors | Twilio-side or transport failure | `transient` |
78
+ | 20003, 20404 (auth/SID misconfig) | Bad credentials or missing resource | `config` |
79
+
80
+ Unknown codes default to `transient` (safe for one retry) and are logged with the raw
81
+ code so the map can be extended. `config` failures additionally `logger.error` at send
82
+ time — they mean the app is misconfigured, not that the message was undeliverable.
83
+
84
+ ### Status callback mapping (Phase 2)
85
+
86
+ | Twilio `MessageStatus` | `DeliveryEvent.status` | `CommsMessage.status` |
87
+ |---|---|---|
88
+ | `queued`, `accepted`, `sending`, `sent` | — (no event; already `sent`) | `sent` |
89
+ | `delivered` | `delivered` | `delivered` |
90
+ | `undelivered`, `failed` | `failed` (+ `errorCode` from `ErrorCode` param) | `failed` |
91
+
92
+ Inbound `STOP`/`START` keyword webhooks (same inbound-webhooks registration) map to
93
+ `OptOutEvent {channel: "sms", reason: "sms-stop"}` / opt-in, feeding the consumer's
94
+ `onOptOut` hook and `CommsMessage.metadata` on subsequent 21610 failures.
95
+
96
+ ## Models / APIs / Notifications / UI
97
+
98
+ None new — Phase 2 adds one webhook route via the inbound-webhooks framework.
99
+
100
+ ## Phases
101
+
102
+ 1. **Send path:** provider, config resolution, E.164 validation, error-code
103
+ classification map, `withApiErrorHandling` wrapping, console deep-link metadata, tests
104
+ with mocked Twilio client; example-backend env-gated registration; docs.
105
+ 2. **Delivery callbacks + opt-outs** *(gated on inbound-webhooks IP)*: status callback
106
+ route with Twilio signature verification, statuses → `DeliveryEvent` →
107
+ `CommsMessage.status` updates (with `errorCode`), STOP/START keyword handling →
108
+ `OptOutEvent` → `onOptOut`.
109
+
110
+ ## Feature Flags & Migrations
111
+
112
+ None.
113
+
114
+ ## Activity Log & User Updates
115
+
116
+ All sends logged to `CommsMessage` with provider `twilio`.
117
+
118
+ ## Not Included / Future Work
119
+
120
+ - Inbound SMS (replies) handling beyond STOP/START keywords.
121
+ - Alphanumeric sender IDs, international compliance tooling.
122
+ - Terreno-managed opt-out suppression list — the adapter emits `OptOutEvent`; storing and
123
+ enforcing preferences is consumer-side (`beforeSend`) until `notification-center` lands.
124
+
125
+ ## Files to Create / Modify
126
+
127
+ - `comms/src/adapters/twilioSms.ts` + tests
128
+ - `comms/package.json` — optional peer `twilio`
129
+ - `example-backend/src/server.ts` — env-gated registration
130
+ - `docs/reference/comms.md`, `docs/reference/environment-variables.md`
131
+
132
+ ## Task List
133
+
134
+ See [docs/tasks/comms-adapter-twilio-sms.md](../tasks/comms-adapter-twilio-sms.md).
135
+
136
+ ## Acceptance Criteria
137
+
138
+ - [ ] `sendSms` delivers via messaging service or from-number config, preferring the
139
+ messaging service when both are present.
140
+ - [ ] A non-E.164 destination fails with a 400 `APIError` before any Twilio call.
141
+ - [ ] Twilio API errors produce `accepted: false` results with `errorCode` and a correct
142
+ `errorClass` per the classification table; the full Twilio error payload lands in
143
+ `CommsMessage.metadata`; nothing throws through the facade.
144
+ - [ ] Permanent-class failures (21610, 30003, …) are never retried inline and appear as
145
+ non-retryable in the admin dashboard; transient-class failures retry once and stay
146
+ manually retryable.
147
+ - [ ] Each send stores `metadata.consoleUrl` linking the message SID to the Twilio
148
+ console.
149
+ - [ ] Consumer `onError` fires with the classified `SendResult` on every failed send
150
+ (mocked-client test).
151
+ - [ ] Apps not using the adapter do not install the `twilio` SDK.
152
+ - [ ] (Phase 2) Status callbacks flip `CommsMessage.status` to `delivered`/`failed` (with
153
+ `errorCode`) under verified Twilio signatures.
154
+ - [ ] (Phase 2) An inbound STOP fires `onOptOut` with `reason: "sms-stop"`; a subsequent
155
+ send to that number records 21610 as `permanent`.
@@ -0,0 +1,99 @@
1
+ # Implementation Plan: Comms adapter — Twilio Verify (OTP)
2
+
3
+ **Status:** Draft
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1021
5
+ **Priority:** Medium
6
+ **Effort:** Small batch
7
+ **Owner:** unassigned
8
+ **Created:** 2026-08-09
9
+ **Program:** [B2B platform](b2b-platform-program.md)
10
+ **Depends on:** [comms-abstraction](comms-abstraction.md)
11
+ **RTK deprecation flag:** None — backend only
12
+
13
+ ## Goal
14
+
15
+ Implement `VerificationProvider` on Twilio Verify so Terreno apps get managed one-time-code
16
+ flows (SMS and email channels) without storing, expiring, or rate-limiting codes
17
+ themselves. This is the delivery channel the future `mfa-step-up-auth` work builds on, and
18
+ it is immediately usable for phone-number verification.
19
+
20
+ ## Non-Goals
21
+
22
+ - The MFA enrollment/step-up product itself (`mfa-step-up-auth` item).
23
+ - TOTP (app-authenticator) support — that is code-local (`otpauth`), not a Verify concern.
24
+ - Custom code storage or self-managed rate limiting (Verify owns both).
25
+
26
+ ## Decisions
27
+
28
+ | Question | Decision |
29
+ |----------|----------|
30
+ | SDK | `twilio` optional peer (shared with the SMS adapter), subpath `@terreno/comms/adapters/twilioVerify` |
31
+ | Service config | `TWILIO_VERIFY_SERVICE_SID` env or constructor option |
32
+ | Channels | `sms` and `email` (Verify's email channel requires a configured Verify email integration; documented, not automated) |
33
+ | Abuse control | Rely on Verify's built-in rate limits; add Terreno-side per-user attempt caps only when `rate-limiting` IP lands |
34
+ | Logging | Start/check attempts logged to `CommsMessage` with channel `verification`, never logging the code |
35
+ | Error classification | Verify errors map to the abstraction taxonomy: 20429/429 and 5xx → `transient`; max-attempts, expired, and invalid-destination → `permanent`; missing/invalid service SID → `config` |
36
+ | Admin dashboard support | Verification rows appear in the [comms-admin-dashboard](comms-admin-dashboard.md) with `errorCode`/`errorClass` and a `metadata.consoleUrl` deep link to the Verify service logs — but the `verification` channel is **never retryable** from admin (codes expire; the user restarts the flow) |
37
+ | Hooks | `onError` fires on failed starts/checks like any other channel; `beforeSend` can gate verification starts (e.g. per-user cool-downs) |
38
+
39
+ ## Architecture
40
+
41
+ ```typescript
42
+ // @terreno/comms/adapters/twilioVerify
43
+ export class TwilioVerifyProvider implements VerificationProvider {
44
+ readonly id = "twilio-verify";
45
+ constructor(options?: {accountSid?: string; authToken?: string; verifyServiceSid?: string});
46
+ startVerification(options: {to: string; channel: "sms" | "email"}): Promise<SendResult>;
47
+ checkVerification(options: {to: string; code: string}): Promise<{valid: boolean; error?: string}>;
48
+ }
49
+ ```
50
+
51
+ `checkVerification` maps Verify's `approved` status to `valid: true`; `pending`, `expired`,
52
+ and max-attempt states return `valid: false` with reason in the result error, letting
53
+ callers throw the appropriate `APIError`.
54
+
55
+ ## Models / APIs / Notifications / UI
56
+
57
+ None new. Consumer routes (e.g. `POST /auth/verifyPhone`) arrive with the features that
58
+ need them; this IP ships the provider plus example-backend registration only.
59
+
60
+ ## Phases
61
+
62
+ Single phase.
63
+
64
+ ## Feature Flags & Migrations
65
+
66
+ None.
67
+
68
+ ## Activity Log & User Updates
69
+
70
+ Verification attempts logged to `CommsMessage` (no codes; destination fully redacted by core,
71
+ with the verification channel stored in metadata).
72
+
73
+ ## Not Included / Future Work
74
+
75
+ - MFA enrollment, recovery codes, biometric step-up (`mfa-step-up-auth`).
76
+ - WhatsApp/voice channels.
77
+ - Android SMS autofill hash configuration (`react-native-otp-verify`, decision D7).
78
+
79
+ ## Files to Create / Modify
80
+
81
+ - `comms/src/adapters/twilioVerify.ts` + tests (mocked Twilio client)
82
+ - `example-backend/src/server.ts` — env-gated registration
83
+ - `docs/reference/comms.md`, `docs/reference/environment-variables.md`
84
+
85
+ ## Task List
86
+
87
+ See [docs/tasks/comms-adapter-twilio-verify.md](../tasks/comms-adapter-twilio-verify.md).
88
+
89
+ ## Acceptance Criteria
90
+
91
+ - [ ] `startVerification` creates a Verify verification on the configured service for sms
92
+ and email channels.
93
+ - [ ] `checkVerification` returns `valid: true` only for `approved`; expired and
94
+ max-attempt states return `valid: false` with a reason.
95
+ - [ ] Failed starts/checks record `errorCode`/`errorClass` per the classification row and
96
+ fire the consumer `onError` hook; verification rows are marked non-retryable for
97
+ the admin dashboard.
98
+ - [ ] No OTP code ever appears in logs or `CommsMessage` rows; destinations are redacted.
99
+ - [ ] Missing `TWILIO_VERIFY_SERVICE_SID` fails fast at registration with a clear error.
@@ -0,0 +1,180 @@
1
+ # Implementation Plan: Comms admin dashboard (errors, retries, log digging)
2
+
3
+ **Status:** Draft
4
+ **Roadmap issue:** *(not yet seeded — see [roadmap-seed-issues.md](../explanation/roadmap-seed-issues.md))*
5
+ **Priority:** High
6
+ **Effort:** Medium batch
7
+ **Owner:** unassigned
8
+ **Created:** 2026-08-11
9
+ **Program:** [B2B platform](b2b-platform-program.md)
10
+ **Depends on:** [comms-abstraction](comms-abstraction.md) (Phases 1–2, including the hook + error-taxonomy additions); adapters optional but the dashboard is most useful once [comms-adapter-twilio-sms](comms-adapter-twilio-sms.md) and [comms-adapter-sendgrid](comms-adapter-sendgrid.md) land
11
+ **RTK deprecation flag:** Partial — screens use the generated SDK; migrate with syncdb like other admin screens
12
+
13
+ ## Goal
14
+
15
+ Make the admin panel the single place to **operate** the comms layer: see errors and
16
+ failure rates at a glance, filter and dig into individual delivery logs (attempt history,
17
+ provider error codes, raw provider metadata), and retry failed sends with one click —
18
+ across every channel and provider, without touching Twilio/SendGrid consoles or raw Mongo.
19
+
20
+ The comms-abstraction IP ships a minimal read-only `GET /comms/messages` explorer. This IP
21
+ upgrades that into a real operations dashboard, following the same pattern the AI package
22
+ uses (`addAiRequestsExplorerRoutes`) but with mutation (retry) and much richer filtering.
23
+
24
+ ## Non-Goals
25
+
26
+ - Building the messages themselves (comms-abstraction) or provider integrations (adapter
27
+ IPs).
28
+ - Charts beyond simple count cards — themed chart components arrive with
29
+ `charts-and-dashboards`; the stats endpoint is designed so charts can be added then.
30
+ - Broadcast/bulk *composition* from admin (sending new messages, campaign tooling).
31
+ - User-facing notification history (`notification-center` item).
32
+ - Cross-service log aggregation (Sentry/Cloud Logging stay the deep-debug tools; the
33
+ dashboard links out via `providerMessageId`).
34
+
35
+ ## Decisions
36
+
37
+ | Question | Decision |
38
+ |----------|----------|
39
+ | Where do the routes live? | `@terreno/comms` (`src/routes/commsDashboard.ts`) — they operate on `CommsMessage`, which the comms package owns. `@terreno/admin-backend` stays model-generic |
40
+ | Where does the UI live? | `@terreno/admin-frontend` (`CommsDashboard*` components), rendered inside `AdminShellLayout` like the rest of the admin panel, and wired into example-frontend + admin-spa |
41
+ | Retry semantics | Retry creates a **new** `CommsMessage` row linked back via `retriedFromId`; the original row is never mutated except `retriedById`. Sends go through the normal `commsService` facade so hooks, logging, and error taxonomy all apply |
42
+ | What is retryable? | `status: "failed" \| "bounced"` **and** `errorClass !== "permanent"` (permanent failures — invalid number, unsubscribed, suppressed address — get a disabled button with the reason) **and** a retained payload exists. `verification` channel is never retryable (codes expire; users restart the flow) |
43
+ | Payload retention for retry | `CommsApp` option `retainPayloadDays` (default 30) keeps the rendered message body on the `CommsMessage` row, passed through the `redactPayload` hook before storage; without a retained payload the retry button is disabled with the reason |
44
+ | Filtering | Server-side query params, not client-side table filtering — delivery logs get large. Free-text `q` matches `subject`, `error`, and `to` (last-4 for redacted destinations) |
45
+ | Bulk retry | `POST /comms/messages/retryMany` takes the same filter object as the list route plus a hard cap (default 100 per call) so an admin can re-drive an outage window without scripting |
46
+ | Stats | One aggregation endpoint with day buckets per channel/provider/status; rendered as count cards now, charts later |
47
+
48
+ ## Architecture
49
+
50
+ ```
51
+ @terreno/comms
52
+ src/routes/commsDashboard.ts # list/detail/retry/retryMany/stats (replaces commsExplorer.ts)
53
+ src/commsService.ts # retryMessage({messageId, req}) added to the facade
54
+
55
+ @terreno/admin-frontend
56
+ src/comms/CommsDashboardScreen.tsx # cards + filter bar + DataTable
57
+ src/comms/CommsMessageDetail.tsx # attempt timeline + raw metadata + retry
58
+ src/comms/CommsStatusBadge.tsx # status → Badge mapping
59
+ src/comms/useCommsDashboardApi.tsx # RTK Query hooks (pattern: useAdminApi)
60
+ ```
61
+
62
+ ### Retry flow
63
+
64
+ ```
65
+ Admin clicks Retry
66
+ → POST /comms/messages/:id/retry (IsAdmin)
67
+ → commsService.retryMessage: validates retryability, loads retained payload
68
+ → normal send path (beforeSend/onSend/onError hooks fire with isRetry: true)
69
+ → new CommsMessage row {retriedFromId: original._id}
70
+ → original row gets {retriedById: new._id}
71
+ → response returns the new row; UI navigates to it
72
+ ```
73
+
74
+ ## Models
75
+
76
+ No new models. Uses the `CommsMessage` fields added by the comms-abstraction IP
77
+ (`errorCode`, `errorClass`, `attempts[]`, `attemptCount`, `lastAttemptAt`,
78
+ `retriedFromId`, `retriedById`, `templateId`, retained `payload`).
79
+
80
+ ## APIs
81
+
82
+ All routes `Permissions.IsAdmin`, built with `createOpenApiBuilder`, in `/openapi.json`.
83
+
84
+ | Method | Path | Notes |
85
+ |---|---|---|
86
+ | GET | `/comms/messages` | Paginated list. Filters: `channel`, `provider`, `status`, `errorClass`, `errorCode`, `userId`, `to`, `templateId`, `retriedFromId`, `startDate`, `endDate`, free-text `q`; sort `-created` default |
87
+ | GET | `/comms/messages/:id` | Full detail: attempts array, raw provider `metadata`, retained payload (post-redaction), linked retry rows |
88
+ | POST | `/comms/messages/:id/retry` | Re-send; 400 with a stable `code` (`comms-retry-not-retryable`, `comms-retry-payload-expired`, `comms-retry-channel-unconfigured`) when not retryable |
89
+ | POST | `/comms/messages/retryMany` | Body: same filter object as list + `limit` (cap 100); returns `{retried, skipped: [{id, reason}]}` |
90
+ | GET | `/comms/stats` | Aggregation: counts by `channel` × `provider` × `status` with day buckets over `startDate`/`endDate` (default 7d); includes failure rate per provider |
91
+
92
+ ## Notifications
93
+
94
+ None. (A failure-rate alert notifier is Future Work — see below.)
95
+
96
+ ## UI
97
+
98
+ All screens live in `@terreno/admin-frontend`, use `@terreno/ui` components only, and
99
+ render on the `AdminShellLayout` canvas (`Page` with `color="transparent"`, `padding={0}`).
100
+
101
+ - **`CommsDashboardScreen`**
102
+ - Summary `Card` row from `/comms/stats`: sent / delivered / failed / bounced for the
103
+ selected range, failure rate per provider, with an error-colored card when the failure
104
+ rate exceeds 5%.
105
+ - Filter bar: `SelectField`s for channel/provider/status/errorClass, `TextField`
106
+ (search icon) for `q`, `DateTimeField` pair for the range. Filters map 1:1 to list
107
+ query params and persist in the URL so views are shareable.
108
+ - `DataTable`: created, channel, provider, to (redacted), subject/preview, status
109
+ (`CommsStatusBadge`), errorCode, attemptCount; row click → detail; inline Retry
110
+ `IconButton` on retryable rows.
111
+ - Loading, error, and empty states per admin-frontend conventions.
112
+ - **`CommsMessageDetail`**
113
+ - Header: status badge, channel/provider, recipient, timestamps, linked user
114
+ (`AdminRefField` → User), `retriedFromId`/`retriedById` links.
115
+ - **Attempt timeline**: one row per `attempts[]` entry — timestamp, provider,
116
+ `providerMessageId` (rendered as an external link to the provider console when the
117
+ adapter supplies `consoleUrl` in metadata), errorCode + error message.
118
+ - **Log digging**: collapsible JSON viewer (`Accordion` + `MarkdownView` code block) for
119
+ retained payload and raw provider `metadata` — everything the adapter recorded, post
120
+ redaction.
121
+ - **Retry** `Button` (`withConfirmation`) — disabled with a tooltip reason when not
122
+ retryable.
123
+
124
+ ## Phases
125
+
126
+ 1. **Backend routes:** upgrade the explorer to `commsDashboard.ts` (filters, detail,
127
+ stats), `retryMessage` on the facade, retry + retryMany routes, supertest coverage for
128
+ every filter and every non-retryable reason.
129
+ 2. **Admin screens:** `CommsDashboardScreen`, `CommsMessageDetail`, badge + hooks; wire
130
+ into example-frontend admin routes and admin-spa nav; regenerate SDK; frontend
131
+ verification with screenshots.
132
+ 3. **Bulk retry + docs:** `retryMany` UI (filtered "Retry all failed" with count
133
+ confirmation), `docs/reference/comms.md` dashboard section, rulesync.
134
+
135
+ ## Feature Flags & Migrations
136
+
137
+ None. New routes and screens only; `CommsMessage` fields ship in comms-abstraction.
138
+
139
+ ## Activity Log & User Updates
140
+
141
+ Every retry is itself a `CommsMessage` row (with `retriedFromId` and the acting admin in
142
+ `metadata.retriedByUserId`), so the audit trail is the same surface the dashboard shows.
143
+
144
+ ## Not Included / Future Work
145
+
146
+ - Failure-rate alerting (Slack/Google Chat notifier when a provider's failure rate spikes)
147
+ — small follow-up once `/comms/stats` exists.
148
+ - Charts on the stats endpoint (`charts-and-dashboards`).
149
+ - Editing a message before retry ("retry with edits").
150
+ - Automatic scheduled retries with backoff — arrives with `job-queues`; this IP is
151
+ explicitly *manual, admin-driven* retry.
152
+
153
+ ## Files to Create / Modify
154
+
155
+ - `comms/src/routes/commsDashboard.ts` (+ tests) — replaces `commsExplorer.ts`
156
+ - `comms/src/commsService.ts` — `retryMessage`
157
+ - `admin-frontend/src/comms/*` (+ tests)
158
+ - `example-frontend/app/admin/comms/*` — routes; `store/openApiSdk.ts` via `bun run sdk`
159
+ - `admin-spa` — nav entry
160
+ - `docs/reference/comms.md`, `docs/reference/admin.md`
161
+
162
+ ## Task List
163
+
164
+ See [docs/tasks/comms-admin-dashboard.md](../tasks/comms-admin-dashboard.md).
165
+
166
+ ## Acceptance Criteria
167
+
168
+ - [ ] An admin can filter delivery logs by channel, provider, status, error class, error
169
+ code, user, recipient, template, date range, and free text — server-side, paginated.
170
+ - [ ] The detail view shows every attempt with timestamp, provider message id, error code
171
+ and message, plus the raw provider metadata and retained payload in a JSON viewer.
172
+ - [ ] Retry re-sends through the configured provider, creates a linked `CommsMessage` row,
173
+ fires the normal lifecycle hooks with `isRetry: true`, and records the acting admin.
174
+ - [ ] Non-retryable messages (permanent failure, expired payload, unconfigured channel,
175
+ verification channel) show a disabled retry with the specific reason; the route
176
+ returns 400 with the matching stable error `code`.
177
+ - [ ] `retryMany` re-drives at most the cap per call and reports skipped ids with reasons.
178
+ - [ ] `/comms/stats` counts match the filtered list totals for the same range.
179
+ - [ ] All routes are `IsAdmin`; non-admin requests get 403 (supertest coverage).
180
+ - [ ] Screens verified in the running example app with screenshots attached to the PR.
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Deploy to GCP (Generalized) + `deploy-gcp` Skill
2
2
 
3
3
  **Status:** Approved — decisions recorded (2026-07-29)
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1005
4
5
  **Priority:** High
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Deploy to Vercel + `deploy-vercel` Skill
2
2
 
3
3
  **Status:** Draft — open Vercel spike TODOs; other decisions recorded (2026-07-29)
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1012
4
5
  **Priority:** High
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Deployment Foundation
2
2
 
3
3
  **Status:** Draft — blocking questions open
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1011
4
5
  **Priority:** High
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Reference Documentation Coverage
2
2
 
3
3
  **Status:** Draft — blocked on PR #869
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1009
4
5
  **Priority:** High
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: AI-First Tutorials
2
2
 
3
3
  **Status:** Draft — blocked on PR #869
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1010
4
5
  **Priority:** High
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned
@@ -1,6 +1,7 @@
1
1
  # Implementation Plan: Examples, Demo, and Test Coverage
2
2
 
3
3
  **Status:** Draft — key decisions recorded (2026-07-29)
4
+ **Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1016
4
5
  **Priority:** Medium
5
6
  **Effort:** Big batch
6
7
  **Owner:** unassigned