@terreno/mcp 0.31.0 → 56.0.0-beta.2

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 (116) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +8 -9
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  5. package/dist/docs/upgrades/56.0.0-beta.2.md +59 -0
  6. package/dist/docs/versioned/next/README.md +2 -0
  7. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
  8. package/dist/docs/versioned/next/how-to/README.md +2 -0
  9. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  10. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
  11. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
  12. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
  13. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
  14. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
  15. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
  16. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  17. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  18. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  19. package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  20. package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  21. package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  22. package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  23. package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  24. package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  25. package/dist/docs/versioned/next/reference/README.md +2 -0
  26. package/dist/docs/versioned/next/reference/comms.md +132 -0
  27. package/dist/docs/versioned/next/reference/environment-variables.md +9 -0
  28. package/dist/docs/versioned/next/reference/syncdb.md +456 -0
  29. package/dist/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  30. package/dist/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  31. package/dist/docs/versioned/next/tasks/comms-abstraction.md +29 -16
  32. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
  33. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
  34. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
  35. package/dist/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  36. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  37. package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  38. package/dist/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  39. package/dist/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  40. package/package.json +3 -3
  41. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
  42. package/src/docs/upgrades/56.0.0-beta.2.md +59 -0
  43. package/src/docs/versioned/next/README.md +2 -0
  44. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
  45. package/src/docs/versioned/next/how-to/README.md +2 -0
  46. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
  47. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
  48. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
  49. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
  50. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
  51. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
  52. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
  53. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  54. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  55. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  56. package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
  57. package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
  58. package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
  59. package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
  60. package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
  61. package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
  62. package/src/docs/versioned/next/reference/README.md +2 -0
  63. package/src/docs/versioned/next/reference/comms.md +132 -0
  64. package/src/docs/versioned/next/reference/environment-variables.md +9 -0
  65. package/src/docs/versioned/next/reference/syncdb.md +456 -0
  66. package/src/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
  67. package/src/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
  68. package/src/docs/versioned/next/tasks/comms-abstraction.md +29 -16
  69. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
  70. package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
  71. package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
  72. package/src/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  73. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  74. package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
  75. package/src/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
  76. package/src/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
  77. package/dist/__tests__/bootstrap.test.d.ts +0 -2
  78. package/dist/__tests__/bootstrap.test.d.ts.map +0 -1
  79. package/dist/__tests__/bootstrap.test.js +0 -299
  80. package/dist/__tests__/bootstrap.test.js.map +0 -1
  81. package/dist/__tests__/databaseQuery.test.d.ts +0 -2
  82. package/dist/__tests__/databaseQuery.test.d.ts.map +0 -1
  83. package/dist/__tests__/databaseQuery.test.js +0 -83
  84. package/dist/__tests__/databaseQuery.test.js.map +0 -1
  85. package/dist/__tests__/docsRoot.test.d.ts +0 -2
  86. package/dist/__tests__/docsRoot.test.d.ts.map +0 -1
  87. package/dist/__tests__/docsRoot.test.js +0 -59
  88. package/dist/__tests__/docsRoot.test.js.map +0 -1
  89. package/dist/__tests__/inferPackages.test.d.ts +0 -2
  90. package/dist/__tests__/inferPackages.test.d.ts.map +0 -1
  91. package/dist/__tests__/inferPackages.test.js +0 -35
  92. package/dist/__tests__/inferPackages.test.js.map +0 -1
  93. package/dist/__tests__/preload.d.ts +0 -2
  94. package/dist/__tests__/preload.d.ts.map +0 -1
  95. package/dist/__tests__/preload.js +0 -12
  96. package/dist/__tests__/preload.js.map +0 -1
  97. package/dist/__tests__/prompts.test.d.ts +0 -2
  98. package/dist/__tests__/prompts.test.d.ts.map +0 -1
  99. package/dist/__tests__/prompts.test.js +0 -295
  100. package/dist/__tests__/prompts.test.js.map +0 -1
  101. package/dist/__tests__/resources.test.d.ts +0 -2
  102. package/dist/__tests__/resources.test.d.ts.map +0 -1
  103. package/dist/__tests__/resources.test.js +0 -352
  104. package/dist/__tests__/resources.test.js.map +0 -1
  105. package/dist/__tests__/search.test.d.ts +0 -2
  106. package/dist/__tests__/search.test.d.ts.map +0 -1
  107. package/dist/__tests__/search.test.js +0 -163
  108. package/dist/__tests__/search.test.js.map +0 -1
  109. package/dist/__tests__/tools.test.d.ts +0 -2
  110. package/dist/__tests__/tools.test.d.ts.map +0 -1
  111. package/dist/__tests__/tools.test.js +0 -540
  112. package/dist/__tests__/tools.test.js.map +0 -1
  113. package/dist/__tests__/upgradeGuide.test.d.ts +0 -2
  114. package/dist/__tests__/upgradeGuide.test.d.ts.map +0 -1
  115. package/dist/__tests__/upgradeGuide.test.js +0 -22
  116. package/dist/__tests__/upgradeGuide.test.js.map +0 -1
@@ -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.
@@ -0,0 +1,126 @@
1
+ # Research: Migrate CI/CD to CircleCI
2
+
3
+ **Status:** Research complete — decisions recorded in IP `migrate-cicd-to-circleci.md`
4
+ **Date:** 2026-08-17
5
+ **Repo:** FlourishHealth/terreno (`master` @ `1e3e79a6`)
6
+ **Branch:** `cursor/migrate-cicd-circleci-2b6d`
7
+
8
+ ## Scope statement
9
+
10
+ Investigate what “migrate all CI/CD to CircleCI” means for Terreno: inventory every GitHub Actions workflow, classify what is portable vs GitHub-native, map secrets/OIDC/deploy targets, and surface **candidate** CircleCI architectures with tradeoffs. No implementation decisions are committed here.
11
+
12
+ ## Current state (findings)
13
+
14
+ ### Scale
15
+
16
+ - **~36** hand-maintained `.github/workflows/*.yml` workflows
17
+ - **4** agentic `*.lock.yml` pipelines (gh-aw / Copilot), compiled from `.md` sources
18
+ - **No** `.circleci/` today
19
+ - **No** `.github/actions/` composites — Bun setup + cache is copy-pasted per workflow
20
+ - Helper scripts live under `.github/workflows/scripts/` (EAS/fingerprint) and `.github/scripts/`
21
+
22
+ ### Category inventory
23
+
24
+ | Category | Examples | CircleCI portability |
25
+ |----------|----------|----------------------|
26
+ | Package CI | `api-ci`, `ui-ci`, `rtk-ci`, `ai-ci`, `comms-ci`, `mcp-server-ci`, example-* | High |
27
+ | Web E2E | `e2e-ci`, `admin-spa-ci`, `admin-spa-integration` (Playwright + Mongo) | High–medium |
28
+ | Mobile E2E | `demo-appium-ci` (Android + macos-15 iOS), `maestro-e2e` | Medium–low (macOS/Android emulator parity) |
29
+ | Expo/EAS | `eas-pr`, `eas-dev-build`, `fingerprint-gate` | Medium (CLI portable; PR labels/comments GitHub-centric) |
30
+ | Deploy GCP | `cd.yml`, `preview-cleanup` | Medium–hard (OIDC is GitHub-issuer only today) |
31
+ | Deploy Netlify | `demo-deploy`, `frontend-example-deploy`, `docs-deploy` | High–medium |
32
+ | Publish | `publish-on-tag`, `publish-feature-flags-manual` | Medium–hard (token push to master, Zoom, cross-workflow dispatch) |
33
+ | Policy | `repo-policies`, `rulesync-check`, `dco` | High (DCO is GitHub-PR metadata) |
34
+ | Security | `codeql-analysis` | Low (GitHub Code Scanning / Security tab) |
35
+ | GitHub-native | `dependabot-auto-merge`, `triage`, `roadmap-generate` | Low–none |
36
+ | Agentic | `architectural-pr-review`, `agentics-maintenance`, `*.lock.yml` | None (gh-aw, `pull_request_target`, Copilot) |
37
+
38
+ ### Shared CI patterns
39
+
40
+ 1. `oven-sh/setup-bun@v2` (`bun-version: latest`, sometimes pinned `1.3.10` for EAS)
41
+ 2. `actions/cache` on `~/.bun/install/cache` keyed by OS + ref + lockfile
42
+ 3. `bun install --frozen-lockfile` then package `lint` / `compile` / `test(:coverage|:ci)`
43
+ 4. Mongo via `supercharge/mongodb-github-action` (image `mirror.gcr.io/library/mongo`) for api/ai/example-backend/e2e
44
+ 5. Most package CI is **`on: push` with path filters**, not `pull_request` — checks appear when the PR head branch is pushed
45
+ 6. Path filtering also uses `dorny/paths-filter` inside larger workflows (`cd.yml`, deploys)
46
+
47
+ ### Deploy / release targets
48
+
49
+ | Target | Mechanism |
50
+ |--------|-----------|
51
+ | GCP `flourish-terreno` | Cloud Run (backend, tasks, mcp), Artifact Registry, Infra Manager terraform |
52
+ | Auth to GCP | Workload Identity Federation — issuer `https://token.actions.githubusercontent.com` (`terraform/modules/github_oidc`) |
53
+ | Preview cleanup | Still uses `GCP_SA_KEY` JSON (not WIF) |
54
+ | Netlify | demo, example-frontend, docs sites via `nwtgck/actions-netlify` |
55
+ | npm | Tag-driven publish of ~12 `@terreno/*` packages |
56
+ | EAS Cloud | PR updates/builds + manual workflow dispatch |
57
+ | GitHub Deployments API | CD + Netlify preview environments |
58
+
59
+ ### Secrets / vars (union, migration-relevant)
60
+
61
+ **Secrets:** `NPM_TOKEN`, `EXPO_TOKEN`, `NETLIFY_*`, `GCP_SA_KEY`, `MCP_SENTRY_DSN`, `E2E_*`, `CURSOR_API_KEY`, `REPO_ADMIN_TOKEN`, `ROADMAP_PROJECT_TOKEN`, `ZOOM_WEBHOOK_*`, plus gh-aw Copilot tokens on lockfiles.
62
+
63
+ **Vars:** `GCP_WIF_PROVIDER_PROD`, `GCP_TF_ADMIN_SA_PROD`, `GCP_CD_DEPLOYER_SA_PROD`, `GCP_TF_PROJECT_ID_PROD`, `GCP_INFRA_MANAGER_LOCATION`, `ARCHITECTURAL_REVIEW_MODEL`, `TERRENO_PROJECT_NUMBER`.
64
+
65
+ ### Branch protection (documented)
66
+
67
+ `docs/explanation/repository-settings.md`: require status checks including **Repository policies**, **package CI**, and **Rulesync Check** relevant to the change. Path-filtered checks are not always present — any CircleCI cutover must remap required checks carefully.
68
+
69
+ ### External / CircleCI platform notes
70
+
71
+ - Official migration guide: map Actions → CircleCI jobs/workflows; Actions Marketplace ≈ Orb Registry ([CircleCI migrate from GitHub](https://circleci.com/docs/guides/migrate/migrating-from-github/)).
72
+ - Monorepo best practice: **dynamic config** (`setup: true`) + **path-filtering** orb so only affected packages run ([2026 monorepo guidance](https://www.devopsness.com/blog/circleci-best-practices-2026-07-19)).
73
+ - `circleci/node` orb **≥7.2.0** has first-class Bun (`install-bun`, frozen lockfile, cache) ([node-orb Bun support](https://github.com/oven-sh/bun/discussions/22830)).
74
+ - GCP OIDC from CircleCI is supported but requires a **new** Workload Identity provider (not a drop-in for the current GitHub-only module).
75
+
76
+ ## Candidate options (not chosen)
77
+
78
+ ### A. Full cutover (literal “all”)
79
+
80
+ Move every runnable pipeline to CircleCI; disable GHA workflows.
81
+
82
+ - **Pros:** Single CI vendor; one secrets home; one config language.
83
+ - **Cons:** Breaks CodeQL Security tab, Dependabot auto-merge, gh-aw agentic bots, issue triage, roadmap generate, `pull_request_target` review, GitHub Deployments UX unless reimplemented via API. Highest risk and cost.
84
+
85
+ ### B. Hybrid “CI/CD on CircleCI, GitHub stays for GitHub-native”
86
+
87
+ Move package CI, E2E, Netlify, GCP CD, EAS triggers, publish to CircleCI. Keep CodeQL, Dependabot, DCO/triage/roadmap, and all gh-aw agentic workflows on GitHub Actions.
88
+
89
+ - **Pros:** Matches “migrate CI/CD” intent without destroying OSS/agentic GitHub integrations; clear ownership split.
90
+ - **Cons:** Two systems forever; dual required-check configuration; doc/contrib complexity.
91
+
92
+ ### C. Phased hybrid with long dual-run
93
+
94
+ Same end-state as B, but phases: (1) mirror package CI, (2) Netlify, (3) EAS/fingerprint, (4) GCP OIDC + CD, (5) publish last. GHA remains required until each phase proves green.
95
+
96
+ - **Pros:** Lowest blast radius; easy rollback; validates CircleCI org/contexts/credits before cutting release path.
97
+ - **Cons:** Temporary double spend; longer calendar; discipline needed to delete GHA when done.
98
+
99
+ ### D. CircleCI for compute-heavy only; keep CD/publish on GHA
100
+
101
+ Only migrate lint/test/Playwright/Appium; leave `cd.yml` + `publish-on-tag.yml` on Actions.
102
+
103
+ - **Pros:** Avoids OIDC rewrite and release double-publish risk; still reduces GHA minute pressure if that is the pain.
104
+ - **Cons:** Does not fulfill “all CI/CD”; two systems for the most critical paths.
105
+
106
+ ## Recommended research framing (for questions, not a plan decision)
107
+
108
+ Option **C** is the lowest-risk path that still delivers a real CircleCI CD home; Option **A** is only viable if product/security accepts losing or re-homing GitHub-native surfaces. Option **D** is a scope cut, not a full migration.
109
+
110
+ ## Open questions (fed into Step 3)
111
+
112
+ 1. Literal “all” vs hybrid (what stays on GitHub)?
113
+ 2. Dual-run duration and when GHA workflows are deleted?
114
+ 3. CircleCI org/project already exist? Free vs Performance plan? Credit budget?
115
+ 4. In-scope: GCP CD + npm publish in v1, or defer?
116
+ 5. macOS Appium iOS — CircleCI macOS resource class vs keep on GHA?
117
+ 6. Who owns secrets migration and branch-protection remapping?
118
+ 7. Roadmap tracking issue after Approved?
119
+
120
+ ## References
121
+
122
+ - Inventory of workflows: `.github/workflows/`
123
+ - GCP OIDC: `terraform/modules/github_oidc/`
124
+ - Deploy docs: `docs/explanation/deployment-architecture-gcp.md`, `terraform/README.md`
125
+ - Branch protection: `docs/explanation/repository-settings.md`
126
+ - CircleCI: [Migrating from GitHub](https://circleci.com/docs/guides/migrate/migrating-from-github/), node orb Bun support
@@ -0,0 +1,269 @@
1
+ # Implementation Plan: Migrate CI/CD to CircleCI
2
+
3
+ **Status:** Draft — decisions recorded; awaiting Approval
4
+ **Discussion:** _(none)_
5
+ **Roadmap issue:** _(pending — hand off via `roadmap-item` when Approved)_
6
+ **Linear:** _(none)_
7
+ **Priority:** High
8
+ **Effort:** Large (cross-cutting infra; many workflows)
9
+ **Owner:** unassigned (CircleCI org/plan: requester)
10
+ **Created:** 2026-08-17
11
+ **Branch:** `cursor/migrate-cicd-circleci-2b6d`
12
+ **Research:** [`migrate-cicd-to-circleci-research.md`](migrate-cicd-to-circleci-research.md)
13
+ **Depends on:** CircleCI org + paid plan created by requester; GCP project access for OIDC terraform
14
+
15
+ ## Goal
16
+
17
+ Move Terreno’s CI/CD from GitHub Actions to CircleCI end-to-end: every pipeline that builds, tests, deploys, publishes, or gates PRs runs on CircleCI. Use a dual-run cutover (GHA stays required until the matching CircleCI workflow is green, then delete the GHA workflow). End state: no `.github/workflows/*.yml` CI/CD jobs remain; GitHub keeps only what CircleCI cannot replace (if any residual is accepted, document it explicitly under Not Included).
18
+
19
+ ## Non-Goals
20
+
21
+ - Changing what tests assert (only where they run), except for runner-parity fixes.
22
+ - Migrating sibling repos (Flourish, Zapling, etc.).
23
+ - Redesigning product deploy topology (Cloud Run / Netlify / EAS stay; only the orchestrator changes).
24
+ - Shipping Appium **iOS** on CircleCI in **v1** (deferred to a late phase; Android Appium may move earlier if ubuntu parity is easy).
25
+ - Rewriting gh-aw agentic *prompt* content; only re-home or replace the *runner*.
26
+
27
+ ## Decisions
28
+
29
+ Recorded 2026-08-17 from Blend clarification.
30
+
31
+ | # | Question | Decision |
32
+ |---|----------|----------|
33
+ | CC1 | Scope | **A — Full cutover.** All CI/CD moves to CircleCI; GHA workflows deleted after dual-run proves green. |
34
+ | CC2 | Cutover style | **A — Dual-run.** Keep GHA required checks until the CircleCI twin is trusted, then remove GHA. |
35
+ | CC3 | v1 must include | **GCP CD + terraform**, **npm publish-on-tag**, **Netlify** (demo / example-frontend / docs), **EAS PR + fingerprint gate**. Not Appium iOS in v1. |
36
+ | CC4 | CircleCI account | Requester creates the CircleCI plan/org and links the GitHub repo. |
37
+ | CC5 | Branch protection | Remapping required checks is in scope; maintainers apply GitHub settings when CircleCI check names are stable. |
38
+ | CC6 | Config shape | **A — Dynamic config** (`setup: true`) + **path-filtering** orb. |
39
+
40
+ ## Architecture
41
+
42
+ ### Target topology
43
+
44
+ ```mermaid
45
+ flowchart TB
46
+ subgraph gh [GitHub]
47
+ PUSH[push / PR / tag / schedule]
48
+ BP[branch protection required checks]
49
+ end
50
+
51
+ subgraph cci [CircleCI]
52
+ SETUP[setup workflow path-filtering]
53
+ CONT[continue config]
54
+ PKG[package CI + policy]
55
+ E2E[Playwright e2e]
56
+ NET[Netlify deploys]
57
+ EAS[EAS + fingerprint]
58
+ GCP[GCP CD via OIDC]
59
+ PUB[npm publish on tag]
60
+ MOB[Appium / Maestro late phase]
61
+ REP[replacements: CodeQL alt, bots, triage]
62
+ end
63
+
64
+ PUSH --> SETUP --> CONT
65
+ CONT --> PKG & E2E & NET & EAS & GCP & PUB & MOB & REP
66
+ PKG & E2E & EAS --> BP
67
+ GCP --> CR[Cloud Run / Artifact Registry / Infra Manager]
68
+ NET --> NL[Netlify sites]
69
+ PUB --> NPM[npmjs @terreno/*]
70
+ EAS --> EXPO[EAS Cloud]
71
+ ```
72
+
73
+ ### Config layout
74
+
75
+ ```
76
+ .circleci/
77
+ config.yml # setup: true — path-filtering only
78
+ continue-config.yml # all real workflows/jobs
79
+ src/ # optional: generated fragments if we split by package
80
+ ```
81
+
82
+ - Use `circleci/path-filtering` to set boolean parameters (`run-api`, `run-ui`, `run-cd`, …) from changed paths (mirror today’s path filters).
83
+ - Use `circleci/node` orb (≥7.2.0) for Bun install + cache (`pkg-manager: bun`).
84
+ - Shared commands: `bun_install`, `start_mongo` (Docker service or `cimg` + mongo service), `compile_workspace_deps`.
85
+ - Prefer a **private org orb** later for reuse; v1 may inline commands in `continue-config.yml` to ship faster, then extract.
86
+
87
+ ### Secrets: CircleCI Contexts
88
+
89
+ | Context | Contents (from current GHA) |
90
+ |---------|------------------------------|
91
+ | `terreno-npm` | `NPM_TOKEN` |
92
+ | `terreno-netlify` | `NETLIFY_AUTH_TOKEN`, site IDs |
93
+ | `terreno-expo` | `EXPO_TOKEN` |
94
+ | `terreno-gcp` | CircleCI OIDC (no long-lived key for CD); migrate `GCP_SA_KEY` off preview-cleanup onto OIDC |
95
+ | `terreno-e2e` | `E2E_TOKEN_SECRET`, `E2E_REFRESH_TOKEN_SECRET`, `E2E_SESSION_SECRET` |
96
+ | `terreno-release` | `REPO_ADMIN_TOKEN`, `ZOOM_WEBHOOK_URL`, `ZOOM_WEBHOOK_TOKEN` |
97
+ | `terreno-agentic` | `CURSOR_API_KEY` (and replacements for Copilot/gh-aw tokens if re-homed) |
98
+ | `terreno-github-api` | PAT with `pull-requests`, `deployments`, `contents` as needed for comments / Deployments API / roadmap |
99
+
100
+ Restrict context access by project + environment (e.g. `release` context only on tag workflows).
101
+
102
+ ### GCP identity (blocking infra for Phase GCP)
103
+
104
+ Today `terraform/modules/github_oidc` trusts **only** `https://token.actions.githubusercontent.com`. CircleCI CD requires:
105
+
106
+ 1. New Workload Identity pool provider for CircleCI’s OIDC issuer.
107
+ 2. Attribute condition on org/project/VCS origin.
108
+ 3. Bind `roles/iam.workloadIdentityUser` for deployer + terraform SAs.
109
+ 4. Dual-trust during dual-run (GitHub + CircleCI providers both valid), then remove GitHub provider when GHA CD is deleted.
110
+
111
+ Prefer extending the module (`circleci_oidc` or rename to `ci_oidc` with multiple providers) over a one-off in Flourish terraform only — keep consumer docs honest.
112
+
113
+ ### GitHub ↔ CircleCI integration
114
+
115
+ - CircleCI GitHub App (or OAuth) on `FlourishHealth/terreno`.
116
+ - Status checks reported to GitHub for branch protection (job names documented in `docs/explanation/repository-settings.md`).
117
+ - PR comments / labels / Deployments API: call GitHub REST via `gh` or `curl` with `terreno-github-api` context (replaces `GITHUB_TOKEN` action privileges).
118
+ - Tag + schedule triggers: CircleCI pipeline triggers (or GitHub → CircleCI API webhook) for cron jobs currently on GHA.
119
+
120
+ ### Dual-run protocol (every phase)
121
+
122
+ 1. Land CircleCI twin; name check `cci/<old-job-name>` or stable CircleCI job name.
123
+ 2. Add CircleCI check as **non-required** optional signal; keep GHA required.
124
+ 3. Observe green on `master` + representative PRs.
125
+ 4. Flip branch protection: require CircleCI; unrequire GHA.
126
+ 5. Delete the GHA workflow file in the same PR window as the protection flip (or immediately after).
127
+ 6. Never leave tag publish listening on **both** systems.
128
+
129
+ ## Models / APIs / Notifications / UI
130
+
131
+ None (infra-only). Zoom release notify stays a webhook call from the publish workflow.
132
+
133
+ ## Phases
134
+
135
+ ### Phase 0 — Prerequisites (human + docs)
136
+
137
+ - Create CircleCI org/plan; link repo; enable dynamic config.
138
+ - Create empty Contexts; document secret migration checklist.
139
+ - Inventory required check names vs today’s GHA job `name:` fields.
140
+
141
+ ### Phase 1 — Foundation
142
+
143
+ - Add `.circleci/config.yml` + `continue-config.yml` skeleton.
144
+ - Path-filtering parameter map covering all packages + terraform + docs + demo.
145
+ - Shared Bun + cache + Mongo patterns.
146
+ - Smoke workflow on every push that only validates config.
147
+
148
+ ### Phase 2 — Package CI + repo policy (v1)
149
+
150
+ Port and dual-run:
151
+
152
+ - `api-ci`, `ai-ci`, `rtk-ci`, `ui-ci`, `ui-demo-ci`, `comms-ci`, `mcp-server-ci`
153
+ - `example-frontend-ci`, `example-backend-ci`, `example-backend-script-runner`, `example-backend-docker`
154
+ - `admin-spa-ci`
155
+ - `repo-policies`, `rulesync-check`, `dco` (DCO via GitHub API + CircleCI)
156
+
157
+ ### Phase 3 — Playwright E2E (v1)
158
+
159
+ - `e2e-ci`, `admin-spa-integration` (Mongo replica set + secrets context).
160
+
161
+ ### Phase 4 — Netlify deploys (v1)
162
+
163
+ - `demo-deploy`, `frontend-example-deploy`, `docs-deploy`
164
+ - Recreate preview aliases + optional GitHub Deployments via API.
165
+ - `preview-cleanup` equivalent for Netlify/GCP previews as applicable.
166
+
167
+ ### Phase 5 — EAS + fingerprint (v1)
168
+
169
+ - Port `eas-pr`, `eas-dev-build`, `fingerprint-gate` scripts under `.circleci/` or keep scripts in `.github/workflows/scripts/` and invoke from CircleCI until relocated to `scripts/ci/`.
170
+ - PR comment + `fingerprint-acknowledged` label via GitHub API.
171
+
172
+ ### Phase 6 — GCP CD + OIDC (v1)
173
+
174
+ - Terraform: CircleCI OIDC provider + dual-trust.
175
+ - Port `cd.yml` (detect → terraform → backend/mcp deploy).
176
+ - Port `preview-cleanup` onto OIDC (eliminate `GCP_SA_KEY`).
177
+ - Dual-run CD carefully (concurrency: one deployer).
178
+
179
+ ### Phase 7 — npm publish (v1)
180
+
181
+ - Port `publish-on-tag` + `publish-feature-flags-manual`.
182
+ - **Single-writer rule:** disable GHA tag trigger in the same change that enables CircleCI tag pipeline.
183
+ - Keep Zoom notify + master version bump + docs cut + demo dispatch.
184
+
185
+ ### Phase 8 — Mobile runners (post-v1 / still required for full cutover)
186
+
187
+ - Maestro (`maestro-e2e`).
188
+ - Appium Android on Linux VM.
189
+ - Appium iOS on CircleCI macOS resource class (image pin parity with `macos-15` intent).
190
+ - Then delete `demo-appium-ci.yml` / `maestro-e2e.yml`.
191
+
192
+ ### Phase 9 — GitHub-native + agentic replacements (full cutover)
193
+
194
+ | Current GHA | Replacement approach |
195
+ |-------------|----------------------|
196
+ | `codeql-analysis` | CircleCI job uploading SARIF to GitHub Code Scanning API **or** third-party SAST orb; preserve Security tab if possible |
197
+ | `dependabot-auto-merge` | Keep Dependabot PRs on GitHub; auto-merge via CircleCI on Dependabot branches **or** Renovate on CircleCI |
198
+ | `triage.yml` | CircleCI pipeline trigger on `issues` via GitHub Apps webhook → CircleCI API, or GitHub Action **stub** only if product accepts residual GHA (conflicts with CC1 — prefer webhook) |
199
+ | `roadmap-generate` | Scheduled CircleCI job with `ROADMAP_PROJECT_TOKEN` |
200
+ | `architectural-pr-review` | CircleCI job + `CURSOR_API_KEY` (lose `pull_request_target` free fork safety — use least privilege + fork policy) |
201
+ | `agentics-maintenance` + `*.lock.yml` gh-aw | Re-home to CircleCI scheduled pipelines calling the same scripts **or** retire features; do not leave gh-aw as the only runner if CC1 holds |
202
+ | `docs-audit` | Scheduled CircleCI job |
203
+
204
+ ### Phase 10 — Cleanup + docs
205
+
206
+ - Delete remaining `.github/workflows/*.yml` (and lock sources if retired).
207
+ - Update `CONTRIBUTING.md`, `docs/explanation/repository-settings.md`, `terraform/README.md`, deploy how-tos, AGENTS.md Cloud CI notes.
208
+ - Archive research; mark IP Complete.
209
+
210
+ ## Feature Flags & Migrations
211
+
212
+ - No product feature flags.
213
+ - Infra migration: dual OIDC trust window; dual CI check window; **no dual tag-publish**.
214
+ - Secret migration checklist is the migration artifact (Contexts populated before jobs go required).
215
+
216
+ ## Activity Log & User Updates
217
+
218
+ - Announce in Discord/Slack/Zoom when required checks flip and when tag publish moves.
219
+ - Changelog: under Unreleased chore — “CI/CD runs on CircleCI”.
220
+
221
+ ## Not Included / Future Work
222
+
223
+ - Private orb publication + versioning pipeline (nice follow-up after Phase 1–2 stabilize).
224
+ - Moving Netlify sites to GCS+CDN (separate deploy architecture IPs).
225
+ - Self-hosted CircleCI runners.
226
+ - Credit-cost optimization beyond path-filtering (test splitting, resource-class tuning).
227
+
228
+ ## Risks & Mitigations
229
+
230
+ | Risk | Mitigation |
231
+ |------|------------|
232
+ | Double npm publish | Single-writer cutover for tags; disable GHA first or in same commit |
233
+ | GCP deploy from wrong IdP | Dual-trust then remove GitHub provider; dry-run terraform preview on CircleCI first |
234
+ | Branch protection gaps during flip | Document exact check names; flip in a dedicated maintainer window |
235
+ | macOS/Appium flake | Deferred to Phase 8; keep GHA Appium until CircleCI macOS proven |
236
+ | gh-aw feature loss | Phase 9 explicit replace-or-retire decision per lockfile before deleting |
237
+ | Credit burn during dual-run | Path filters; timebox dual-run per phase (target: days, not months) |
238
+ | `mcp-server-ci` still filters `main` | Normalize to `master` while porting |
239
+
240
+ ## Files to Create / Modify
241
+
242
+ **Create**
243
+
244
+ - `.circleci/config.yml`
245
+ - `.circleci/continue-config.yml`
246
+ - `docs/how-to/circleci.md` (or `docs/explanation/ci-cd-circleci.md`)
247
+ - `terraform/modules/circleci_oidc/` or extend `github_oidc` → multi-provider `ci_oidc`
248
+ - `scripts/ci/` (relocated EAS/fingerprint helpers)
249
+
250
+ **Modify**
251
+
252
+ - `docs/explanation/repository-settings.md`
253
+ - `terraform/README.md`, Flourish/terreno terraform roots that wire OIDC
254
+ - `CONTRIBUTING.md`, root/CI docs references to GitHub Actions
255
+ - Delete `.github/workflows/*` progressively per phase
256
+
257
+ ## Task List
258
+
259
+ See [`docs/tasks/migrate-cicd-to-circleci.md`](../tasks/migrate-cicd-to-circleci.md).
260
+
261
+ ## Acceptance Criteria
262
+
263
+ - [ ] All v1 workflows (package CI, policy, Playwright e2e, Netlify, EAS/fingerprint, GCP CD, npm publish) are green on CircleCI and required on `master` where applicable.
264
+ - [ ] Matching GHA workflows for those surfaces are deleted; tag publish has exactly one listener.
265
+ - [ ] CircleCI OIDC can deploy Cloud Run without `GCP_SA_KEY` for CD paths.
266
+ - [ ] Path-filtering skips unrelated package jobs on a docs-only change.
267
+ - [ ] `repository-settings.md` lists CircleCI check names; branch protection matches.
268
+ - [ ] Phase 8–9 either complete (full cutover) or are tracked as blocking follow-ups before calling CC1 done.
269
+ - [ ] CONTRIBUTING / terraform / deploy docs no longer instruct maintainers to use GHA for Terreno CI/CD.