@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.
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +8 -9
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
- package/dist/docs/upgrades/56.0.0-beta.2.md +59 -0
- package/dist/docs/versioned/next/README.md +2 -0
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
- package/dist/docs/versioned/next/how-to/README.md +2 -0
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
- package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
- package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
- package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
- package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
- package/dist/docs/versioned/next/reference/README.md +2 -0
- package/dist/docs/versioned/next/reference/comms.md +132 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +9 -0
- package/dist/docs/versioned/next/reference/syncdb.md +456 -0
- package/dist/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
- package/dist/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
- package/dist/docs/versioned/next/tasks/comms-abstraction.md +29 -16
- package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
- package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
- package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
- package/dist/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
- package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
- package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
- package/dist/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
- package/dist/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
- package/package.json +3 -3
- package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +1 -1
- package/src/docs/upgrades/56.0.0-beta.2.md +59 -0
- package/src/docs/versioned/next/README.md +2 -0
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +21 -0
- package/src/docs/versioned/next/how-to/README.md +2 -0
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +445 -0
- package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +28 -50
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -2
- package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +98 -15
- package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +63 -13
- package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +65 -12
- package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +9 -3
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
- package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +52 -47
- package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +303 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +252 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +468 -420
- package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +425 -0
- package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +800 -0
- package/src/docs/versioned/next/reference/README.md +2 -0
- package/src/docs/versioned/next/reference/comms.md +132 -0
- package/src/docs/versioned/next/reference/environment-variables.md +9 -0
- package/src/docs/versioned/next/reference/syncdb.md +456 -0
- package/src/docs/versioned/next/superpowers/plans/2026-07-28-admin-mobile-navigation.md +97 -0
- package/src/docs/versioned/next/superpowers/specs/2026-07-28-admin-mobile-navigation-design.md +36 -0
- package/src/docs/versioned/next/tasks/comms-abstraction.md +29 -16
- package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +17 -7
- package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +17 -7
- package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +3 -3
- package/src/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
- package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
- package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +10 -10
- package/src/docs/versioned/next/tasks/syncdb-codegen.md +65 -0
- package/src/docs/versioned/next/tasks/syncdb-local-first.md +434 -117
- package/dist/__tests__/bootstrap.test.d.ts +0 -2
- package/dist/__tests__/bootstrap.test.d.ts.map +0 -1
- package/dist/__tests__/bootstrap.test.js +0 -299
- package/dist/__tests__/bootstrap.test.js.map +0 -1
- package/dist/__tests__/databaseQuery.test.d.ts +0 -2
- package/dist/__tests__/databaseQuery.test.d.ts.map +0 -1
- package/dist/__tests__/databaseQuery.test.js +0 -83
- package/dist/__tests__/databaseQuery.test.js.map +0 -1
- package/dist/__tests__/docsRoot.test.d.ts +0 -2
- package/dist/__tests__/docsRoot.test.d.ts.map +0 -1
- package/dist/__tests__/docsRoot.test.js +0 -59
- package/dist/__tests__/docsRoot.test.js.map +0 -1
- package/dist/__tests__/inferPackages.test.d.ts +0 -2
- package/dist/__tests__/inferPackages.test.d.ts.map +0 -1
- package/dist/__tests__/inferPackages.test.js +0 -35
- package/dist/__tests__/inferPackages.test.js.map +0 -1
- package/dist/__tests__/preload.d.ts +0 -2
- package/dist/__tests__/preload.d.ts.map +0 -1
- package/dist/__tests__/preload.js +0 -12
- package/dist/__tests__/preload.js.map +0 -1
- package/dist/__tests__/prompts.test.d.ts +0 -2
- package/dist/__tests__/prompts.test.d.ts.map +0 -1
- package/dist/__tests__/prompts.test.js +0 -295
- package/dist/__tests__/prompts.test.js.map +0 -1
- package/dist/__tests__/resources.test.d.ts +0 -2
- package/dist/__tests__/resources.test.d.ts.map +0 -1
- package/dist/__tests__/resources.test.js +0 -352
- package/dist/__tests__/resources.test.js.map +0 -1
- package/dist/__tests__/search.test.d.ts +0 -2
- package/dist/__tests__/search.test.d.ts.map +0 -1
- package/dist/__tests__/search.test.js +0 -163
- package/dist/__tests__/search.test.js.map +0 -1
- package/dist/__tests__/tools.test.d.ts +0 -2
- package/dist/__tests__/tools.test.d.ts.map +0 -1
- package/dist/__tests__/tools.test.js +0 -540
- package/dist/__tests__/tools.test.js.map +0 -1
- package/dist/__tests__/upgradeGuide.test.d.ts +0 -2
- package/dist/__tests__/upgradeGuide.test.d.ts.map +0 -1
- package/dist/__tests__/upgradeGuide.test.js +0 -22
- 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.
|