diff --git a/docs/WINDSURF/SERVICE_CONSOLIDATION_ROADMAP.md b/docs/WINDSURF/SERVICE_CONSOLIDATION_ROADMAP.md index 563429cc..c7d29e0f 100644 --- a/docs/WINDSURF/SERVICE_CONSOLIDATION_ROADMAP.md +++ b/docs/WINDSURF/SERVICE_CONSOLIDATION_ROADMAP.md @@ -3,7 +3,8 @@ > **Goal:** Merge `billing-service`, `growth-service`, and `tracker-service` into `platform-service` so we have one unified Fastify service for all common platform concerns. `extraction-service` stays separate (Python sidecar). > > **Created:** 2026-02-14 -> **Estimated effort:** 3–4 days +> **Reviewed:** 2026-02-14 (thorough gap analysis — see Critical Gaps section) +> **Estimated effort:** 4–5 days > **Blocked by:** Nothing — can start immediately --- @@ -22,6 +23,138 @@ --- +## Critical Gaps Found During Review + +> These MUST be addressed during the merge or features/tests will break. + +### Gap 1: Product ID Naming Inconsistency + +Services export product ID differently — modules reference different names: + +| Service | Export Name | Source | +|---------|-----------|--------| +| **platform-service** | `PRODUCT_ID` | `loadProductIdentity().productId` from `@bytelyst/config` | +| **growth-service** | `PRODUCT_ID` | same as platform ✅ | +| **billing-service** | `PRODUCT_ID` | same as platform ✅ | +| **tracker-service** | `DEFAULT_PRODUCT_ID` | `process.env.DEFAULT_PRODUCT_ID \|\| getProductId()` — **different name** ⚠️ | + +**Fix:** When merging tracker modules, change all `DEFAULT_PRODUCT_ID` imports to `PRODUCT_ID` in the copied module files, and add `DEFAULT_PRODUCT_ID` env var support to platform-service's `product-config.ts` for backward compat. + +### Gap 2: Missing Dependencies in Platform-Service + +Platform-service `package.json` is **missing** these deps needed by merged modules: + +| Dep | Needed By | Currently In | +|-----|-----------|-------------| +| `stripe` (^17.5.0) | billing modules (stripe webhooks, checkout) | billing-service, growth-service | +| `@bytelyst/auth` (workspace:*) | tracker modules (`extractAuth`) | tracker-service | +| `@fastify/rate-limit` (^10.3.0) | tracker rate limiting | tracker-service | + +### Gap 3: Billing Internal Key Auth (Global Hook) + +`billing-service/src/server.ts` has a **global** `onRequest` hook: +```typescript +app.addHook('onRequest', async (req, reply) => { + if (path === '/health' || path.includes('/stripe/webhook')) return; + const key = req.headers['x-internal-key']; + if (key !== INTERNAL_KEY) reply.code(401).send(...) +}); +``` +This **cannot** be a global hook after merge — it would block auth, audit, tracker, etc. routes. + +**Fix:** Convert to a Fastify plugin registered only on billing route prefixes, or add `x-internal-key` check inside each billing route handler. + +### Gap 4: Growth Webhooks Library + +`growth-service/src/lib/webhooks.ts` dispatches fire-and-forget HTTP callbacks on invitation redeem. References env vars: +- `WEBHOOK_INVITATION_REDEEMED_URL` +- `WEBHOOK_REFERRAL_STATUS_URL` + +**Fix:** Copy `webhooks.ts` to platform-service `src/lib/`, add both env vars to config schema. + +### Gap 5: Growth Config Requires `STRIPE_SECRET_KEY` + +Growth-service config requires `STRIPE_SECRET_KEY` as **required** (not optional). Platform-service doesn't currently need Stripe at all. + +**Fix:** Add `STRIPE_SECRET_KEY` to platform-service config. Make it **optional** with validation only when billing/growth routes are hit (or make it required after merge since billing always needs it). + +### Gap 6: 17+ Consumer Files Need URL Updates (LysnrAI Repo) + +**Dashboard API clients (TypeScript):** + +| File | Current Env Var | Current Default | +|------|----------------|-----------------| +| `admin-dashboard-web/src/lib/billing-client.ts` | `BILLING_SERVICE_URL` | `http://localhost:4002` | +| `admin-dashboard-web/src/lib/growth-client.ts` | `GROWTH_SERVICE_URL` | `http://localhost:4001` | +| `user-dashboard-web/src/lib/billing-client.ts` | `BILLING_SERVICE_URL` | `http://localhost:4002` | +| `user-dashboard-web/src/lib/growth-client.ts` | `GROWTH_SERVICE_URL` | `http://localhost:4001` | +| `user-dashboard-web/src/app/api/stripe/webhook/route.ts` | `BILLING_SERVICE_URL` | `http://localhost:4002` | +| `admin-dashboard-web/src/app/api/stripe/config/route.ts` | — | `http://localhost:4002` inline | +| `admin-dashboard-web/src/lib/stripe-context.tsx` | — | `http://localhost:4002` (3 places) | +| `tracker-dashboard-web/src/app/api/tracker/[...path]/route.ts` | `TRACKER_API_URL` | `http://localhost:4004` | +| `tracker-dashboard-web/src/app/api/auth/login/route.ts` | `PLATFORM_API_URL` | `http://localhost:4003` ✅ | +| `tracker-dashboard-web/src/app/api/auth/me/route.ts` | `PLATFORM_API_URL` | `http://localhost:4003` ✅ | + +**Python clients (desktop + backend):** + +| File | Current Env Var | Current Default | +|------|----------------|-----------------| +| `backend/src/clients/billing_client.py` | `BILLING_SERVICE_URL` | `http://localhost:4002` | +| `src/cloud/api_sync.py` | `BILLING_SERVICE_URL` | `http://localhost:4002` | +| `src/cloud/plan_resolver.py` | `BILLING_SERVICE_URL` | `http://localhost:4002` | + +All these must change to `PLATFORM_SERVICE_URL` / `http://localhost:4003`. + +### Gap 7: Ops Status Health Check Route + +`admin-dashboard-web/src/app/api/ops/status/route.ts` checks health of 5 individual services on separate ports. After consolidation, billing/growth/tracker entries must be removed — they'll all respond on platform-service's `/health`. + +### Gap 8: Stripe Webhook Test Hardcodes Port + +`user-dashboard-web/src/__tests__/stripe-webhook.test.ts` sets: +```typescript +process.env.BILLING_SERVICE_URL = 'http://localhost:4002'; +expect(url).toBe('http://localhost:4002/api/stripe/webhook'); +``` +Must update to port 4003. + +### Gap 9: Load Test Scripts + +- `tests/load/billing-service.js` — `BASE_URL || "http://localhost:4002"` +- `tests/load/growth-service.js` — `BASE_URL || "http://localhost:4001"` + +Must update defaults to port 4003. + +### Gap 10: Stripe Documentation + +- `docs/STRIPE_SETUP_GUIDE.md` — references `localhost:4002/api/stripe/webhook` +- `docs/BILLING_GAPS_ANALYSIS.md` — references `localhost:4002/api/stripe/webhook` + +### Gap 11: LysnrAI Services Stubs + +`learning_voice_ai_agent/services/` contains `.env.example` stubs for each service: +- `services/billing-service/.env.example` +- `services/growth-service/.env.example` +- `services/tracker-service/.env.example` +- `services/platform-service/.env.example` + +After consolidation, remove billing/growth/tracker stubs, keep platform-service with merged env vars. + +### Gap 12: Mobile Apps + +No references to old service ports found in `mobile_app/` — **no changes needed**. ✅ +Mobile apps call the Python backend (`localhost:8000`), which calls billing-service. The Python backend client (Gap 6) handles the redirection. + +### Route Path Collision Check ✅ + +All services use unique route prefixes — **no collisions**: +- platform: `/auth/*`, `/audit/*`, `/notifications/*`, `/flags/*`, `/ratelimit/*`, `/blob/*`, `/devices/*` +- billing: `/subscriptions/*`, `/usage/*`, `/plans/*`, `/licenses/*`, `/payments/*`, `/stripe/*` +- growth: `/invitations/*`, `/referrals/*`, `/promos/*` +- tracker: `/items/*`, `/comments/*`, `/votes/*`, `/public/*` + +--- + ## Current State ``` @@ -75,13 +208,14 @@ All containers served by one Cosmos client in platform-service: ## Phase 0 — Preparation -> **Goal:** Backup, verify tests pass before any changes. +> **Goal:** Backup, verify tests pass, baseline everything before any changes. - [ ] **0.1** Backup all 3 repos via `/repo_backup-main-branch` - [ ] **0.2** Verify all services build: `pnpm build` -- [ ] **0.3** Verify all tests pass: `pnpm test` -- [ ] **0.4** Document current test counts per service (baseline) -- [ ] **0.5** Create `consolidation` branch (or work on main with incremental commits) +- [ ] **0.3** Verify all tests pass: `pnpm test` (record exact counts per service) +- [ ] **0.4** Baseline test counts: platform ~55, billing ~11, growth ~14, tracker ~45 = **~125 total** +- [ ] **0.5** Run `npx tsc --noEmit` in all 3 dashboards — confirm clean +- [ ] **0.6** Run `python -m pytest tests/ -q` in LysnrAI — confirm Python tests pass --- @@ -95,34 +229,45 @@ All containers served by one Cosmos client in platform-service: - [ ] **1.1.2** Copy `growth-service/src/modules/referrals/` → `platform-service/src/modules/referrals/` - [ ] **1.1.3** Copy `growth-service/src/modules/promos/` → `platform-service/src/modules/promos/` -### 1.2 Fix imports +### 1.2 Copy lib files -- [ ] **1.2.1** Update import paths in copied modules: `../../lib/errors.js`, `../../lib/product-config.js`, `../../lib/cosmos.js` -- [ ] **1.2.2** Copy `growth-service/src/lib/webhooks.ts` → `platform-service/src/lib/webhooks.ts` (used by invitation redeem) -- [ ] **1.2.3** Verify no growth-specific lib files are missed +- [ ] **1.2.1** Copy `growth-service/src/lib/webhooks.ts` → `platform-service/src/lib/webhooks.ts` **(Gap 4)** +- [ ] **1.2.2** Verify growth `product-config.ts` uses same `PRODUCT_ID` export name as platform ✅ -### 1.3 Register routes +### 1.3 Fix imports in copied modules -- [ ] **1.3.1** Add imports to `platform-service/src/server.ts`: `invitationRoutes`, `referralRoutes`, `promoRoutes` -- [ ] **1.3.2** Register routes with `/api` prefix (same as growth-service) +- [ ] **1.3.1** Update all `../../lib/errors.js` → verify same re-export exists in platform-service +- [ ] **1.3.2** Update all `../../lib/product-config.js` → verify `PRODUCT_ID` export matches +- [ ] **1.3.3** Update all `../../lib/cosmos.js` → verify same pattern +- [ ] **1.3.4** Update `../../lib/webhooks.js` references -### 1.4 Merge config +### 1.4 Merge config **(Gap 5)** -- [ ] **1.4.1** Add any growth-specific env vars to `platform-service/src/lib/config.ts` (check for `WEBHOOK_URL`, etc.) -- [ ] **1.4.2** Add growth Cosmos containers to platform-service container registry +- [ ] **1.4.1** Add to `platform-service/src/lib/config.ts`: + - `STRIPE_SECRET_KEY: z.string().optional()` (was required in growth — make optional for now) + - `WEBHOOK_INVITATION_REDEEMED_URL: z.string().optional()` + - `WEBHOOK_REFERRAL_STATUS_URL: z.string().optional()` +- [ ] **1.4.2** Add `stripe` (^17.5.0) to `platform-service/package.json` dependencies +- [ ] **1.4.3** Add growth Cosmos containers to platform-service container registry -### 1.5 Copy tests +### 1.5 Register routes -- [ ] **1.5.1** Copy `growth-service/src/modules/invitations/invitations.test.ts` → platform-service -- [ ] **1.5.2** Copy any other test files from growth-service modules -- [ ] **1.5.3** Fix test import paths +- [ ] **1.5.1** Add imports to `platform-service/src/server.ts`: `invitationRoutes`, `referralRoutes`, `promoRoutes` +- [ ] **1.5.2** Register routes with `/api` prefix (same as growth-service) -### 1.6 Verify +### 1.6 Copy + fix tests -- [ ] **1.6.1** `pnpm --filter @lysnrai/platform-service build` — clean -- [ ] **1.6.2** `pnpm --filter @lysnrai/platform-service test` — all tests pass (existing + growth) -- [ ] **1.6.3** Remove `services/growth-service/` directory -- [ ] **1.6.4** Commit: `refactor: merge growth-service into platform-service` +- [ ] **1.6.1** Copy all growth test files to platform-service +- [ ] **1.6.2** Fix test import paths +- [ ] **1.6.3** Run tests: `pnpm --filter @lysnrai/platform-service test` — **growth tests must pass** + +### 1.7 Verify + remove + +- [ ] **1.7.1** `pnpm --filter @lysnrai/platform-service build` — clean +- [ ] **1.7.2** `pnpm --filter @lysnrai/platform-service test` — all tests pass (existing + growth ≥ 69) +- [ ] **1.7.3** Remove `services/growth-service/` directory +- [ ] **1.7.4** `pnpm install` — workspace resolution updated (no broken refs) +- [ ] **1.7.5** Commit: `refactor: merge growth-service into platform-service` --- @@ -138,35 +283,61 @@ All containers served by one Cosmos client in platform-service: - [ ] **2.1.4** Copy `billing-service/src/modules/licenses/` → `platform-service/src/modules/licenses/` - [ ] **2.1.5** Copy `billing-service/src/modules/stripe/` → `platform-service/src/modules/stripe/` -### 2.2 Fix imports +### 2.2 Handle billing internal key auth **(Gap 3 — CRITICAL)** -- [ ] **2.2.1** Update all import paths in copied modules -- [ ] **2.2.2** Handle billing-specific internal key auth — convert `BILLING_INTERNAL_KEY` hook to route-level guard on billing endpoints (instead of global onRequest hook) +- [ ] **2.2.1** Do NOT copy the global `onRequest` hook from billing-service `server.ts` +- [ ] **2.2.2** Create `platform-service/src/lib/billing-auth.ts` — a Fastify plugin that checks `x-internal-key` header +- [ ] **2.2.3** Register the billing auth plugin ONLY on billing route prefixes: + ```typescript + // In server.ts — wrap billing routes with internal key check + await app.register(async (billingScope) => { + billingScope.addHook('onRequest', billingKeyGuard); + await billingScope.register(subscriptionRoutes, { prefix: '/api' }); + await billingScope.register(usageRoutes, { prefix: '/api' }); + await billingScope.register(planRoutes, { prefix: '/api' }); + await billingScope.register(licenseRoutes, { prefix: '/api' }); + // Note: stripeRoutes has its own webhook signature check — skip internal key + }); + await app.register(stripeRoutes, { prefix: '/api' }); // Outside billing scope + ``` +- [ ] **2.2.4** Verify: auth, audit, tracker, etc. routes are NOT affected by billing key check -### 2.3 Merge config +### 2.3 Fix imports in copied modules -- [ ] **2.3.1** Add billing env vars to `platform-service/src/lib/config.ts`: - - `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_PRO`, `STRIPE_PRICE_ENTERPRISE` - - `BILLING_INTERNAL_KEY`, `BACKEND_URL`, `PLAN_LIMITS_JSON`, `USAGE_WARN_THRESHOLD` -- [ ] **2.3.2** Add billing Cosmos containers to platform-service container registry -- [ ] **2.3.3** Add `stripe` to platform-service `package.json` dependencies +- [ ] **2.3.1** Update all import paths in copied modules +- [ ] **2.3.2** Verify `PRODUCT_ID` export name matches (billing uses same as platform ✅) -### 2.4 Register routes +### 2.4 Merge config -- [ ] **2.4.1** Add imports to `platform-service/src/server.ts` -- [ ] **2.4.2** Register: `subscriptionRoutes`, `usageRoutes`, `planRoutes`, `licenseRoutes`, `stripeRoutes` +- [ ] **2.4.1** Add billing env vars to `platform-service/src/lib/config.ts`: + - `STRIPE_SECRET_KEY` — upgrade from optional (Phase 1) to required + - `STRIPE_WEBHOOK_SECRET: z.string().optional()` + - `STRIPE_PRICE_PRO: z.string().optional()` + - `STRIPE_PRICE_ENTERPRISE: z.string().optional()` + - `BILLING_INTERNAL_KEY: z.string().optional()` + - `BACKEND_URL: z.string().default('http://localhost:8000')` + - `PLAN_LIMITS_JSON: z.string().optional()` + - `USAGE_WARN_THRESHOLD: z.coerce.number().default(0.8)` +- [ ] **2.4.2** Add billing Cosmos containers to container registry -### 2.5 Copy tests +### 2.5 Register routes -- [ ] **2.5.1** Copy all billing test files to platform-service -- [ ] **2.5.2** Fix test import paths +- [ ] **2.5.1** Add imports to `platform-service/src/server.ts` +- [ ] **2.5.2** Register routes with billing auth scope (see 2.2.3) -### 2.6 Verify +### 2.6 Copy + fix tests -- [ ] **2.6.1** `pnpm --filter @lysnrai/platform-service build` — clean -- [ ] **2.6.2** `pnpm --filter @lysnrai/platform-service test` — all tests pass -- [ ] **2.6.3** Remove `services/billing-service/` directory -- [ ] **2.6.4** Commit: `refactor: merge billing-service into platform-service` +- [ ] **2.6.1** Copy all billing test files to platform-service +- [ ] **2.6.2** Fix test import paths +- [ ] **2.6.3** Run tests: billing tests must pass + +### 2.7 Verify + remove + +- [ ] **2.7.1** `pnpm --filter @lysnrai/platform-service build` — clean +- [ ] **2.7.2** `pnpm --filter @lysnrai/platform-service test` — all tests pass (≥ 80) +- [ ] **2.7.3** Remove `services/billing-service/` directory +- [ ] **2.7.4** `pnpm install` — workspace resolution updated +- [ ] **2.7.5** Commit: `refactor: merge billing-service into platform-service` --- @@ -181,82 +352,138 @@ All containers served by one Cosmos client in platform-service: - [ ] **3.1.3** Copy `tracker-service/src/modules/votes/` → `platform-service/src/modules/votes/` - [ ] **3.1.4** Copy `tracker-service/src/modules/public/` → `platform-service/src/modules/public/` -### 3.2 Fix imports +### 3.2 Fix Product ID naming **(Gap 1 — CRITICAL)** -- [ ] **3.2.1** Update all import paths in copied modules -- [ ] **3.2.2** Copy `tracker-service/src/lib/auth.ts` → merge into platform-service's existing auth helper (both use `extractAuth` from `@bytelyst/auth`) +- [ ] **3.2.1** In all 4 copied module dirs, replace `DEFAULT_PRODUCT_ID` → `PRODUCT_ID` in imports +- [ ] **3.2.2** Update `../../lib/product-config.js` imports to match platform-service pattern +- [ ] **3.2.3** Add `DEFAULT_PRODUCT_ID` env var support to `platform-service/src/lib/config.ts` for backward compat +- [ ] **3.2.4** In `product-config.ts`, add: `export const DEFAULT_PRODUCT_ID = PRODUCT_ID;` (alias for tracker compat) -### 3.3 Merge config +### 3.3 Fix auth import -- [ ] **3.3.1** No new env vars needed (tracker uses same Cosmos + JWT vars as platform) -- [ ] **3.3.2** Add tracker Cosmos containers to container registry +- [ ] **3.3.1** Tracker modules import `extractAuth` from `../../lib/auth.js` — verify platform-service has this file +- [ ] **3.3.2** If missing, create `platform-service/src/lib/auth.ts` re-exporting from `@bytelyst/auth` +- [ ] **3.3.3** Add `@bytelyst/auth` (workspace:*) to platform-service `package.json` **(Gap 2)** +- [ ] **3.3.4** Add `@fastify/rate-limit` (^10.3.0) to platform-service `package.json` **(Gap 2)** +- [ ] **3.3.5** Add `jose` if not already present (tracker uses it for JWT — already in platform ✅) -### 3.4 Register routes +### 3.4 Merge config -- [ ] **3.4.1** Add imports to `platform-service/src/server.ts` -- [ ] **3.4.2** Register: `itemRoutes`, `commentRoutes`, `voteRoutes`, `publicRoutes` -- [ ] **3.4.3** Ensure public routes skip auth (they already do internally — verify) +- [ ] **3.4.1** Add `DEFAULT_PRODUCT_ID: z.string().default('lysnrai')` to config schema +- [ ] **3.4.2** Add tracker Cosmos containers to container registry -### 3.5 Copy tests +### 3.5 Register routes -- [ ] **3.5.1** Copy all tracker test files to platform-service -- [ ] **3.5.2** Fix test import paths -- [ ] **3.5.3** Verify: 45+ tracker tests pass +- [ ] **3.5.1** Add imports to `platform-service/src/server.ts` +- [ ] **3.5.2** Register: `itemRoutes`, `commentRoutes`, `voteRoutes`, `publicRoutes` +- [ ] **3.5.3** Public routes must NOT be behind any auth middleware — register at top-level (not inside a scoped plugin) -### 3.6 Verify +### 3.6 Copy + fix tests -- [ ] **3.6.1** `pnpm --filter @lysnrai/platform-service build` — clean -- [ ] **3.6.2** `pnpm --filter @lysnrai/platform-service test` — all 125+ tests pass -- [ ] **3.6.3** Remove `services/tracker-service/` directory -- [ ] **3.6.4** Commit: `refactor: merge tracker-service into platform-service` +- [ ] **3.6.1** Copy all tracker test files to platform-service +- [ ] **3.6.2** Fix test import paths +- [ ] **3.6.3** Fix any `DEFAULT_PRODUCT_ID` references in tests +- [ ] **3.6.4** Run tests: 45+ tracker tests must pass + +### 3.7 Verify + remove + +- [ ] **3.7.1** `pnpm --filter @lysnrai/platform-service build` — clean +- [ ] **3.7.2** `pnpm --filter @lysnrai/platform-service test` — **all 125+ tests pass** +- [ ] **3.7.3** Remove `services/tracker-service/` directory +- [ ] **3.7.4** `pnpm install` — workspace resolution updated +- [ ] **3.7.5** Commit: `refactor: merge tracker-service into platform-service` --- -## Phase 4 — Update Consumers +## Phase 4 — Update Consumers (LysnrAI Repo) -> **Goal:** Update all dashboards, scripts, configs, and docker files that reference the old services. +> **Goal:** Update all dashboards, Python clients, scripts, configs, and docker files that reference the old service ports/URLs. -### 4.1 Dashboard API clients +### 4.1 Dashboard API clients **(Gap 6)** -- [ ] **4.1.1** `admin-dashboard-web/src/lib/billing-client.ts` — change `BILLING_API_URL` → `PLATFORM_API_URL` (port 4003) -- [ ] **4.1.2** `admin-dashboard-web/src/lib/growth-client.ts` — change `GROWTH_API_URL` → `PLATFORM_API_URL` -- [ ] **4.1.3** `user-dashboard-web/src/lib/billing-client.ts` — same change -- [ ] **4.1.4** `tracker-dashboard-web/src/lib/tracker-client.ts` — change `TRACKER_API_URL` → `PLATFORM_API_URL` -- [ ] **4.1.5** Update all dashboard `env.local.example` files — remove old service URLs, add note that everything goes through `PLATFORM_API_URL` +- [ ] **4.1.1** `admin-dashboard-web/src/lib/billing-client.ts` — `BILLING_SERVICE_URL` → `PLATFORM_SERVICE_URL`, default `http://localhost:4003` +- [ ] **4.1.2** `admin-dashboard-web/src/lib/growth-client.ts` — `GROWTH_SERVICE_URL` → `PLATFORM_SERVICE_URL`, default `http://localhost:4003` +- [ ] **4.1.3** `user-dashboard-web/src/lib/billing-client.ts` — same +- [ ] **4.1.4** `user-dashboard-web/src/lib/growth-client.ts` — same +- [ ] **4.1.5** `tracker-dashboard-web/src/app/api/tracker/[...path]/route.ts` — `TRACKER_API_URL` → `PLATFORM_API_URL`, default `http://localhost:4003` -### 4.2 Docker Compose +### 4.2 Stripe proxy + context **(Gap 6)** -- [ ] **4.2.1** `learning_ai_common_plat/docker-compose.yml` — remove billing, growth, tracker service entries; keep platform + extraction -- [ ] **4.2.2** `learning_voice_ai_agent/docker-compose.yml` — same cleanup -- [ ] **4.2.3** Update Traefik labels (all routes go to platform-service) +- [ ] **4.2.1** `user-dashboard-web/src/app/api/stripe/webhook/route.ts` — `BILLING_SERVICE_URL` → `PLATFORM_SERVICE_URL` +- [ ] **4.2.2** `admin-dashboard-web/src/app/api/stripe/config/route.ts` — `billingServiceUrl` default to port 4003 +- [ ] **4.2.3** `admin-dashboard-web/src/lib/stripe-context.tsx` — update all 3 `localhost:4002` references to `localhost:4003` -### 4.3 Run scripts +### 4.3 Ops status route **(Gap 7)** -- [ ] **4.3.1** `learning_voice_ai_agent/run-local-all-services.sh` — remove billing/growth/tracker start commands; update health checks -- [ ] **4.3.2** `.windsurf/workflows/start-all-services.md` — update to reflect 2 services +- [ ] **4.3.1** `admin-dashboard-web/src/app/api/ops/status/route.ts` — remove billing/growth/tracker entries from `SERVICES` array; keep backend + platform + extraction -### 4.4 Environment files +### 4.4 Stripe webhook test **(Gap 8)** -- [ ] **4.4.1** `learning_ai_common_plat/.env.example` — consolidate all env vars into one section for platform-service -- [ ] **4.4.2** Remove references to ports 4001, 4002, 4004 +- [ ] **4.4.1** `user-dashboard-web/src/__tests__/stripe-webhook.test.ts` — change `http://localhost:4002` → `http://localhost:4003` in all 3 places -### 4.5 CI +### 4.5 Python clients **(Gap 6)** -- [ ] **4.5.1** `.github/workflows/ci.yml.disabled` — remove billing/growth/tracker from matrix; only platform + extraction -- [ ] **4.5.2** Delete individual CI workflows: `ci-billing-service.yml`, `ci-growth-service.yml`, `ci-tracker-service.yml` (if they exist) +- [ ] **4.5.1** `backend/src/clients/billing_client.py` — `BILLING_SERVICE_URL` → `PLATFORM_SERVICE_URL`, default `http://localhost:4003` +- [ ] **4.5.2** `src/cloud/api_sync.py` — same +- [ ] **4.5.3** `src/cloud/plan_resolver.py` — same -### 4.6 Verify consumers +### 4.6 Environment files -- [ ] **4.6.1** `npx tsc --noEmit` in admin-dashboard-web -- [ ] **4.6.2** `npx tsc --noEmit` in user-dashboard-web -- [ ] **4.6.3** `npx tsc --noEmit` in tracker-dashboard-web -- [ ] **4.6.4** Commit: `refactor: update consumers for consolidated platform-service` +- [ ] **4.6.1** `learning_voice_ai_agent/.env.example` — replace `BILLING_SERVICE_URL=http://localhost:4002` with `PLATFORM_SERVICE_URL=http://localhost:4003` +- [ ] **4.6.2** `admin-dashboard-web/.env.example` — remove `BILLING_SERVICE_URL`, `GROWTH_SERVICE_URL`; ensure `PLATFORM_SERVICE_URL` present +- [ ] **4.6.3** `admin-dashboard-web/.env.local.example` — same +- [ ] **4.6.4** `user-dashboard-web/.env.example` — same +- [ ] **4.6.5** `user-dashboard-web/.env.local.example` — same +- [ ] **4.6.6** `tracker-dashboard-web/.env.example` — remove `TRACKER_API_URL`, use `PLATFORM_API_URL` +- [ ] **4.6.7** `tracker-dashboard-web/.env.local.example` — same + +### 4.7 LysnrAI service stubs **(Gap 11)** + +- [ ] **4.7.1** Remove `services/billing-service/` directory (just .env.example stub) +- [ ] **4.7.2** Remove `services/growth-service/` directory +- [ ] **4.7.3** Remove `services/tracker-service/` directory +- [ ] **4.7.4** Update `services/platform-service/.env.example` with merged env vars + +### 4.8 Docker Compose (both repos) + +- [ ] **4.8.1** `learning_ai_common_plat/docker-compose.yml` — remove billing, growth, tracker service entries +- [ ] **4.8.2** `learning_voice_ai_agent/docker-compose.yml` — same cleanup +- [ ] **4.8.3** Update Traefik labels (all routes go to platform-service on 4003) +- [ ] **4.8.4** Remove healthcheck entries for ports 4001, 4002, 4004 + +### 4.9 Run scripts + workflows + +- [ ] **4.9.1** `learning_voice_ai_agent/run-local-all-services.sh` — remove billing/growth/tracker start commands; update health checks +- [ ] **4.9.2** `.windsurf/workflows/start-all-services.md` — update to reflect 2 services (platform + extraction) + +### 4.10 Load tests **(Gap 9)** + +- [ ] **4.10.1** `tests/load/billing-service.js` — change default URL to `http://localhost:4003` +- [ ] **4.10.2** `tests/load/growth-service.js` — same + +### 4.11 Stripe docs **(Gap 10)** + +- [ ] **4.11.1** `docs/STRIPE_SETUP_GUIDE.md` — change `localhost:4002` → `localhost:4003` +- [ ] **4.11.2** `docs/BILLING_GAPS_ANALYSIS.md` — same + +### 4.12 CI + +- [ ] **4.12.1** `.github/workflows/ci.yml.disabled` (common-plat) — remove billing/growth/tracker from matrix +- [ ] **4.12.2** Delete individual disabled CI workflows if they exist + +### 4.13 Verify consumers + +- [ ] **4.13.1** `npx tsc --noEmit` in admin-dashboard-web — clean +- [ ] **4.13.2** `npx tsc --noEmit` in user-dashboard-web — clean +- [ ] **4.13.3** `npx tsc --noEmit` in tracker-dashboard-web — clean +- [ ] **4.13.4** Run `vitest` in user-dashboard-web — stripe webhook test passes with new port +- [ ] **4.13.5** Commit in LysnrAI repo: `refactor: update all consumers for consolidated platform-service` --- -## Phase 5 — Documentation & Cleanup +## Phase 5 — Documentation & Final Cleanup -> **Goal:** Update all docs, AGENTS.md, and clean up dead references. +> **Goal:** Update all docs, AGENTS.md, and verify nothing is broken. ### 5.1 Documentation @@ -265,40 +492,41 @@ All containers served by one Cosmos client in platform-service: - [ ] **5.1.3** Update `docs/MIGRATION_GUIDE.md` — single service URL for all API calls - [ ] **5.1.4** Update `docs/ROADMAP.md` — add consolidation as completed item -### 5.2 Package cleanup +### 5.2 Platform-service cleanup -- [ ] **5.2.1** Remove `services/billing-service/` from git (already deleted in Phase 2) -- [ ] **5.2.2** Remove `services/growth-service/` from git (already deleted in Phase 1) -- [ ] **5.2.3** Remove `services/tracker-service/` from git (already deleted in Phase 3) -- [ ] **5.2.4** Run `pnpm install` — workspace resolution updated -- [ ] **5.2.5** Verify `pnpm build` passes across entire workspace +- [ ] **5.2.1** Update `platform-service/package.json` description — include all domains +- [ ] **5.2.2** Update `platform-service/src/server.ts` — description comment lists all 18 modules +- [ ] **5.2.3** Swagger description reflects all modules +- [ ] **5.2.4** Update platform-service `.env.example` — includes Stripe, webhook, billing key vars -### 5.3 Platform-service cleanup +### 5.3 Workspace cleanup -- [ ] **5.3.1** Update `platform-service/package.json` — description, add any missing deps -- [ ] **5.3.2** Update `platform-service/src/server.ts` — description comment -- [ ] **5.3.3** Swagger description reflects all 18 modules +- [ ] **5.3.1** `pnpm install` — no broken workspace refs +- [ ] **5.3.2** Grep entire workspace for `localhost:4001`, `localhost:4002`, `localhost:4004` — must return 0 results +- [ ] **5.3.3** Grep entire workspace for `billing-service`, `growth-service`, `tracker-service` — only docs/history references remain ### 5.4 Final verification - [ ] **5.4.1** `pnpm build` — all packages + platform-service + extraction-service build -- [ ] **5.4.2** `pnpm test` — all 125+ tests pass -- [ ] **5.4.3** `pnpm typecheck` — clean across workspace -- [ ] **5.4.4** Commit: `docs: update all documentation for consolidated platform-service` +- [ ] **5.4.2** `pnpm test` — all 125+ tests pass in platform-service +- [ ] **5.4.3** `pnpm typecheck` — clean across common-plat workspace +- [ ] **5.4.4** `npx tsc --noEmit` — clean across all 3 LysnrAI dashboards +- [ ] **5.4.5** `python -m pytest tests/ -q` — Python tests still pass (billing client URL changed) +- [ ] **5.4.6** Commit: `docs: update all documentation for consolidated platform-service` --- ## Summary -| Phase | What | Effort | Tests Moved | -|-------|------|--------|-------------| -| **0** | Preparation & backup | 30 min | — | -| **1** | Merge growth-service (3 modules) | 2–3 hrs | ~14 | -| **2** | Merge billing-service (5 modules) | 3–4 hrs | ~11 | -| **3** | Merge tracker-service (4 modules) | 3–4 hrs | ~45 | -| **4** | Update consumers (dashboards, docker, scripts, CI) | 2–3 hrs | — | -| **5** | Documentation & cleanup | 1–2 hrs | — | -| **Total** | **5 services → 2** | **~3 days** | **~125+ tests** | +| Phase | What | Effort | Tests Moved | Critical Gaps Addressed | +|-------|------|--------|-------------|------------------------| +| **0** | Preparation & backup | 30 min | — | — | +| **1** | Merge growth-service (3 modules) | 2–3 hrs | ~14 | Gap 4 (webhooks), Gap 5 (Stripe key) | +| **2** | Merge billing-service (5 modules) | 4–5 hrs | ~11 | Gap 3 (internal key auth) | +| **3** | Merge tracker-service (4 modules) | 3–4 hrs | ~45 | Gap 1 (product ID), Gap 2 (deps) | +| **4** | Update consumers (17+ files across repos) | 3–4 hrs | — | Gaps 6–11 | +| **5** | Documentation & final verification | 2–3 hrs | — | — | +| **Total** | **5 services → 2** | **~4–5 days** | **~125+ tests** | **12 gaps addressed** | ## Port Allocation (After) @@ -316,13 +544,17 @@ Each phase has its own commit. If a phase breaks something: 1. `git revert ` to undo that phase 2. The old service code is in git history 3. Backup branches created in Phase 0 +4. Consumers (Phase 4) are updated LAST — services work on old ports until Phase 4 ## Risks & Mitigations | Risk | Mitigation | |------|-----------| -| Route path collisions between services | All services already use unique prefixes (`/auth/*`, `/subscriptions/*`, `/items/*`, etc.) | -| Config schema gets large | Group env vars by domain in config.ts with clear comments | -| Stripe webhook handler needs raw body | Fastify already handles this — just verify after move | -| Billing internal key auth | Convert from global hook to route-level guard on billing endpoints only | -| Public tracker routes skip auth | They already handle auth internally — just verify | +| Route path collisions | Verified ✅ — all services use unique prefixes | +| Config schema gets large | Group env vars by domain with clear section comments | +| Stripe webhook raw body | Fastify handles this — verify after move | +| Billing internal key blocks other routes | Scoped Fastify plugin (Phase 2.2) isolates key check to billing prefixes only | +| Public tracker routes skip auth | Register outside scoped plugins — verify in Phase 3.5.3 | +| Python billing client breaks | Change env var name, keep same API paths — transparent to Python code | +| Stripe webhook test fails | Explicit port update in Phase 4.4 | +| Product ID mismatch | Alias `DEFAULT_PRODUCT_ID = PRODUCT_ID` in Phase 3.2.4 |