# ByteLyst Ecosystem — Post-Refactor Architecture > **Companion diagram:** `ecosystem-after-refactor.drawio` (open in draw.io or VS Code draw.io extension) > > **Date:** 2026-02-12 · **Author:** Cascade --- ## Table of Contents 1. [Ecosystem Overview](#1-ecosystem-overview) 2. [Three-Repo Structure](#2-three-repo-structure) 3. [Common Platform Packages — Detailed](#3-common-platform-packages--detailed) 4. [Component & Service Inventory](#4-component--service-inventory) 5. [Dependency Graph](#5-dependency-graph) 6. [Migration Plan](#6-migration-plan) 7. [Advantages](#7-advantages) 8. [Cautions & Risks](#8-cautions--risks) 9. [Versioning Strategy](#9-versioning-strategy) 10. [Testing Strategy](#10-testing-strategy) 11. [CI/CD Impact](#11-cicd-impact) 12. [Decision Log](#12-decision-log) --- ## 1. Ecosystem Overview The ByteLyst ecosystem consists of **two product repos** and **one shared infrastructure repo**: ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ CLIENT APPLICATIONS │ │ LysnrAI Desktop · iOS · Android · Admin · User Portal · Tracker │ │ MindLyst iOS · Android · Web │ └───────────────────────────────┬──────────────────────────────────────────┘ │ ┌──────────────────┐ ┌────────┴────────┐ ┌──────────────────────────────┐ │ LysnrAI Repo │ │ Common Platform │ │ MindLyst Repo │ │ (voice_ai_agent)│←─│ (common_plat) │─→│ (multimodal_memory_agents) │ │ │ │ @bytelyst/* │ │ │ │ 4 Fastify svcs │ │ 8 npm packages │ │ KMP shared module │ │ 3 Next.js apps │ │ │ │ 3 native apps │ │ 1 FastAPI │ │ Design tokens │ │ 1 Next.js web │ │ 1 Desktop app │ │ (JSON → all) │ │ design-system/ │ │ 2 Mobile apps │ │ │ │ │ └────────┬─────────┘ └─────────────────┘ └──────────────┬───────────────┘ │ │ ┌────────┴─────────────────────────────────────────────────┴───────────────┐ │ AZURE CLOUD INFRASTRUCTURE │ │ Cosmos DB · Blob Storage · Key Vault · Speech · OpenAI · Stripe │ │ Docker Compose (Traefik · Loki · Grafana) · GitHub Actions │ └──────────────────────────────────────────────────────────────────────────┘ ``` --- ## 2. Three-Repo Structure ### 2.1 learning_voice_ai_agent (LysnrAI) **Purpose:** Cross-platform voice-to-text dictation platform. | Component | Tech | Port | Description | |-----------|------|------|-------------| | Desktop App | Python 3.12, tkinter | — | macOS/Windows dictation app | | FastAPI Backend | Python 3.12 | 8000 | REST API, cloud sync | | platform-service | Fastify + TS | 4003 | Auth, audit, flags, notifications, blob, rate limiting | | billing-service | Fastify + TS | 4002 | Subscriptions, plans, usage, licenses, Stripe | | growth-service | Fastify + TS | 4001 | Invitations, referrals, promo codes | | tracker-service | Fastify + TS | 4004 | Feature requests, bugs, votes, public roadmap | | admin-dashboard-web | Next.js 16 | 3001 | Admin panel (users, tokens, audit, themes) | | user-dashboard-web | Next.js 16 | 3002 | User portal (profile, billing, settings, SSO) | | tracker-dashboard-web | Next.js | 3003 | Tracker board, kanban, public roadmap | | iOS App | Swift + SwiftUI | — | Native (no KMP) | | Android App | Kotlin + Compose | — | Native (no KMP) | | Monitoring | Loki + Grafana | — | Health checks, logs, metrics | ### 2.2 learning_multimodal_memory_agents (MindLyst) **Purpose:** Role-based Life OS — multimodal second-brain with AI triage. | Component | Tech | Description | |-----------|------|-------------| | KMP Shared Module | Kotlin Multiplatform | Models, repositories, DI, triage pipeline, API clients | | iOS App | SwiftUI + KMP | Native UI consuming shared Kotlin module | | Android App | Jetpack Compose + KMP | Native UI consuming shared Kotlin module | | Web Dashboard | Next.js 14 | Pages Router, CSS custom properties, landing + dashboard | | design-system/ | JSON + CSS | Canonical tokens, component specs | ### 2.3 learning_ai_common_plat (Common Platform) **Purpose:** Shared npm packages consumed by both product repos. | Package | Type | Primary Consumers | |---------|------|-------------------| | `@bytelyst/errors` | TypeScript | All 4 Fastify services, dashboards | | `@bytelyst/cosmos` | TypeScript | All 4 Fastify services, 2 Next.js dashboards | | `@bytelyst/config` | TypeScript | All 4 Fastify services | | `@bytelyst/auth` | TypeScript | All services + dashboards with JWT | | `@bytelyst/fastify-core` | TypeScript | All 4 Fastify services | | `@bytelyst/api-client` | TypeScript | All 3 Next.js dashboards | | `@bytelyst/react-auth` | TypeScript/React | All 3 Next.js dashboards | | `@bytelyst/design-tokens` | JSON → multi-format | MindLyst (all platforms), potentially LysnrAI dashboards | --- ## 3. Common Platform Packages — Detailed ### 3.1 `@bytelyst/errors` (P0) **What it replaces:** 4 separate `errors.ts` files across services (157 LOC total). ```typescript // Unified error hierarchy export class ServiceError extends Error { constructor(public statusCode: number, message: string) { ... } } export class NotFoundError extends ServiceError { /* 404 */ } export class BadRequestError extends ServiceError { /* 400 */ } export class UnauthorizedError extends ServiceError { /* 401 */ } export class ForbiddenError extends ServiceError { /* 403 */ } export class ConflictError extends ServiceError { /* 409 */ } export class TooManyRequestsError extends ServiceError { /* 429 */ } ``` **Design decision:** Superset of all error types currently used. Services import only what they need. The `details` property (used by billing-service's TooManyRequests) is added to the base class as optional. **Dependencies:** None (leaf package). --- ### 3.2 `@bytelyst/cosmos` (P0) **What it replaces:** 6 separate `cosmos.ts` files (306 LOC total). ```typescript // Core exports export function getCosmosClient(): CosmosClient; export function getDatabase(dbName?: string): Database; export function getContainer(name: string): Container; // Container registry (dashboards use this) export function registerContainers(config: Record): void; export function initializeAllContainers(): Promise; // Types export interface ContainerConfig { partitionKeyPath: string; defaultTtl?: number | null; } ``` **Design decision:** The simple `getContainer(name)` used by microservices works alongside the full registry used by dashboards. Services call `getContainer()` directly; dashboards call `registerContainers()` first to set up the partition key map. **Dependencies:** `@azure/cosmos` (peer dependency). --- ### 3.3 `@bytelyst/config` (P1) **What it replaces:** 4 separate `config.ts` + 4 separate `product-config.ts` (142 LOC total). ```typescript // Base env schema — every Fastify service needs these export const baseEnvSchema = z.object({ PORT: z.coerce.number().default(3000), HOST: z.string().default("0.0.0.0"), NODE_ENV: z.enum(["development", "production", "test"]).default("development"), CORS_ORIGIN: z.string().optional(), SERVICE_NAME: z.string(), COSMOS_ENDPOINT: z.string().min(1), COSMOS_KEY: z.string().min(1), COSMOS_DATABASE: z.string().default("lysnrai"), }); // Extend per-service export function loadConfig(extension: T) { return baseEnvSchema.extend(extension).parse(process.env); } // Product identity export function loadProductIdentity(path?: string): ProductIdentity; export interface ProductIdentity { productId: string; displayName: string; licensePrefix: string; configDirName: string; envVarPrefix: string; } ``` **Design decision:** Each service calls `loadConfig({ STRIPE_SECRET_KEY: z.string(), ... })` to extend the base. The product identity reads from `shared/product.json` or falls back to env vars, eliminating the hardcoded `product-config.ts` copies. **Dependencies:** `zod` (peer dependency). --- ### 3.4 `@bytelyst/auth` (P1) **What it replaces:** 5 separate JWT/auth files (250 LOC total). ```typescript // JWT operations (configurable issuer + expiry) export function createJwtUtils(options: { issuer: string; accessTokenExpiry?: string; // default "1h" refreshTokenExpiry?: string; // default "30d" }): { createAccessToken(payload: TokenPayload): Promise; createRefreshToken(payload: { sub: string }): Promise; verifyToken(token: string): Promise; }; // Fastify middleware export function extractAuth(req: FastifyRequest): Promise; export function requireRole(...roles: string[]): FastifyHook; // Next.js server-side export function getCurrentUser(authHeader: string | null, getUserById: Function): Promise; // Password hashing export function hashPassword(plain: string): Promise; export function verifyPassword(plain: string, hash: string): Promise; // Types export interface AuthPayload { sub: string; email?: string; role?: string; productId?: string; type?: string; } export interface TokenPayload { sub: string; email: string; role: string; type: "access" | "refresh"; } ``` **Design decision:** The `createJwtUtils()` factory pattern allows each service to configure its own issuer (`lysnrai-admin`, `lysnrai-user`, `lysnrai`) while sharing all the logic. Platform-service creates tokens; other services only verify. The `requireRole()` hook is a new addition that multiple services currently implement inline. **Dependencies:** `jose`, `bcryptjs` (peer dependencies). --- ### 3.5 `@bytelyst/fastify-core` (P1) **What it replaces:** 4 separate `server.ts` files (351 LOC total). ```typescript export async function createServiceApp(options: { name: string; version: string; description: string; port?: number; corsOrigin?: string; }): Promise { const app = Fastify({ logger: true }); // CORS await app.register(cors, { origin: ... }); // OpenAPI / Swagger await app.register(swagger, { openapi: { info: { title, version, description } } }); // Prometheus metrics await app.register(metricsPlugin, { endpoint: "/metrics" }); // x-request-id propagation app.addHook("onRequest", requestIdHook); // Health endpoint app.get("/health", healthHandler(options.name, options.version)); // ServiceError-aware error handler app.setErrorHandler(serviceErrorHandler); return app; } export async function startService(app: FastifyInstance, port: number, host?: string): Promise; ``` **Design decision:** After calling `createServiceApp()`, each service just registers its route modules and calls `startService()`. The server.ts in each service shrinks from ~85 lines to ~15 lines. The `healthHandler` returns the standard `{ status, service, version, timestamp, requestId }` shape that the monitoring health-check script expects. **Dependencies:** `fastify`, `@fastify/cors`, `@fastify/swagger`, `fastify-metrics`, `@bytelyst/errors`, `@bytelyst/config` (peer dependencies). --- ### 3.6 `@bytelyst/api-client` (P2) **What it replaces:** 5+ separate fetch wrapper implementations (200 LOC total). ```typescript export function createApiClient(options: { baseUrl: string; getToken?: () => string | null; defaultHeaders?: Record; }): { // Throws on error (for service-to-service) fetch(path: string, options?: RequestInit): Promise; // Returns { data, error } tuple (for UI components) safeFetch(path: string, options?: RequestInit): Promise<{ data: T | null; error: string | null }>; }; ``` **Design decision:** Two fetch modes: throwing (used by server-side service clients like `billing-client.ts`, `growth-client.ts`) and safe (used by React components via `api.ts`). The `getToken()` callback handles auth header injection without coupling to any specific storage mechanism. **Dependencies:** None (uses native `fetch`). --- ### 3.7 `@bytelyst/react-auth` (P2) **What it replaces:** 3 separate `auth-context.tsx` files (450 LOC total). ```typescript export function createAuthProvider(config: { storagePrefix: string; // "admin" | "portal" | "tracker" loginEndpoint: string; // "/api/auth/login" mapResponseToUser: (data: any) => TUser; enableSSO?: boolean; }): { AuthProvider: React.FC<{ children: ReactNode }>; useAuth: () => AuthContextValue; }; export interface BaseUser { id?: string; email: string; name: string; role: string; } export interface AuthContextValue { user: TUser | null; isAuthenticated: boolean; isLoading: boolean; login: (email: string, password: string) => Promise; logout: () => void; getAccessToken: () => string | null; } ``` **Design decision:** Factory pattern produces a fully-typed `AuthProvider` + `useAuth()` pair. Each dashboard provides its own `TUser` type and storage prefix. SSO cookie support (used by user-dashboard) is opt-in via `enableSSO`. Registration support (also user-dashboard only) can be added as an optional config callback. **Dependencies:** `react` (peer dependency), `@bytelyst/api-client`. --- ### 3.8 `@bytelyst/design-tokens` (P3) **What it replaces:** 5 manually-synced token files across MindLyst. ``` packages/design-tokens/ ├── tokens/ │ └── bytelyst.tokens.json ← SINGLE SOURCE OF TRUTH ├── generated/ ← Auto-generated (committed or gitignored) │ ├── tokens.css ← CSS custom properties (--ml-*) │ ├── tokens.ts ← TypeScript constants │ ├── tokens.kt ← Kotlin object (MindLystTokens) │ └── tokens.swift ← Swift structs (MindLystColors/Spacing/Radius) ├── scripts/ │ └── generate.ts ← Reads JSON, emits all formats └── package.json ``` **Design decision:** The canonical `bytelyst.tokens.json` (migrated from MindLyst's existing `mindlyst.tokens.json`) is the single source. A generation script produces platform-specific files. Consumers either: - **Import the generated file** directly (CSS, TS) - **Copy the generated file** into their project as a build step (Kotlin, Swift) This eliminates the manual sync problem where token changes require updating 5 files. **Dependencies:** None at runtime; `typescript` for the generation script. --- ## 4. Component & Service Inventory ### 4.1 All Services (Post-Refactor) | Service | Repo | Port | Shared Packages Used | |---------|------|------|---------------------| | platform-service | LysnrAI | 4003 | errors, cosmos, config, auth, fastify-core | | billing-service | LysnrAI | 4002 | errors, cosmos, config, fastify-core | | growth-service | LysnrAI | 4001 | errors, cosmos, config, fastify-core | | tracker-service | LysnrAI | 4004 | errors, cosmos, config, auth, fastify-core | | FastAPI Backend | LysnrAI | 8000 | (Python — separate concern) | | admin-dashboard | LysnrAI | 3001 | cosmos, auth, api-client, react-auth | | user-dashboard | LysnrAI | 3002 | cosmos, auth, api-client, react-auth | | tracker-dashboard | LysnrAI | 3003 | api-client, react-auth | | MindLyst Web | MindLyst | 3050 | design-tokens | | MindLyst iOS | MindLyst | — | design-tokens (generated Swift) | | MindLyst Android | MindLyst | — | design-tokens (generated Kotlin) | ### 4.2 Azure Infrastructure | Service | Resource | Used By | |---------|----------|---------| | Cosmos DB | cosmos-mywisprai (Serverless) | All TS services + dashboards | | Blob Storage | bytelystblobs (6 containers) | platform-service, desktop, backend | | Key Vault | kv-mywisprai | Desktop, mobile, backend | | Speech | mywisprai-speech (F0) | Desktop, LysnrAI mobile | | Azure OpenAI | mywisprai-openai-sweden (S0) | Desktop, backend | | OpenAI API | api.openai.com | MindLyst KMP (triage, Whisper) | | Stripe | Payments API | billing-service, growth-service | ### 4.3 DevOps Components | Component | Location | Scope | |-----------|----------|-------| | Docker Compose | LysnrAI root | All services + Loki + Grafana + Traefik | | Health Check | services/monitoring/ | Aggregates all /health endpoints | | GitHub Actions | .github/workflows/ (9 files) | Per-service CI | | run-local-all-services.sh | LysnrAI root | Dev startup script | --- ## 5. Dependency Graph ``` @bytelyst/design-tokens (standalone — no deps) │ └─→ MindLyst iOS/Android/Web @bytelyst/errors (standalone — no deps) │ ├─→ @bytelyst/fastify-core ├─→ All 4 Fastify services └─→ All 3 Next.js dashboards (via API routes) @bytelyst/cosmos (depends on: @azure/cosmos) │ ├─→ All 4 Fastify services └─→ admin-dashboard, user-dashboard @bytelyst/config (depends on: zod) │ ├─→ @bytelyst/fastify-core ├─→ @bytelyst/auth └─→ All 4 Fastify services @bytelyst/auth (depends on: jose, bcryptjs, @bytelyst/config) │ ├─→ @bytelyst/api-client ├─→ platform-service (creates tokens) ├─→ tracker-service (verifies tokens) └─→ admin/user dashboards (server-side auth) @bytelyst/fastify-core (depends on: fastify, @bytelyst/errors, @bytelyst/config) │ └─→ All 4 Fastify services @bytelyst/api-client (no runtime deps) │ ├─→ @bytelyst/react-auth └─→ All 3 dashboards @bytelyst/react-auth (depends on: react, @bytelyst/api-client) │ └─→ All 3 Next.js dashboards ``` **Key insight:** The graph is a clean DAG (directed acyclic graph) with no circular dependencies. Leaf packages (`errors`, `cosmos`, `design-tokens`) can be extracted first with zero risk. --- ## 6. Migration Plan ### Phase 1: Foundation (Week 1) 1. **Initialize `learning_ai_common_plat`** as pnpm workspace monorepo - `pnpm init`, `pnpm-workspace.yaml`, `tsconfig.base.json` - Shared `vitest` config, `ruff.toml` (for future Python packages) 2. **Extract `@bytelyst/errors`** (P0, 1 hour) - Copy `platform-service/src/lib/errors.ts` as starting point - Add `ConflictError` (from tracker) and `TooManyRequestsError` (from billing) - Add optional `details` field to base `ServiceError` - Write tests (port from service tests) - Update all 4 services: `import { ServiceError, ... } from "@bytelyst/errors"` 3. **Extract `@bytelyst/cosmos`** (P0, 2 hours) - Merge service version (simple) + dashboard version (registry + TTL) - Parameterize default database name - Write tests (mock Cosmos client) - Update all 6 consumers ### Phase 2: Service Infrastructure (Week 2) 4. **Extract `@bytelyst/config`** (P1, 2 hours) - Create `baseEnvSchema` from common fields - Add `loadConfig()` extension function - Add `loadProductIdentity()` from `shared/product.json` - Update all 4 services + remove 4 `product-config.ts` files 5. **Extract `@bytelyst/auth`** (P1, 3 hours) - Create `createJwtUtils()` factory - Port `extractAuth()` Fastify hook - Port `getCurrentUser()` for Next.js - Port `hashPassword()` / `verifyPassword()` - Critical: test all auth flows end-to-end after migration 6. **Extract `@bytelyst/fastify-core`** (P1, 3 hours) - Create `createServiceApp()` factory - Port request-id hook, health handler, error handler - Refactor all 4 `server.ts` files to use factory - Verify Docker builds still pass ### Phase 3: Dashboard Libraries (Week 3) 7. **Extract `@bytelyst/api-client`** (P2, 2 hours) - Create `createApiClient()` with both fetch modes - Update dashboard client files to use shared client - Service-specific methods stay in their respective client files 8. **Extract `@bytelyst/react-auth`** (P2, 3 hours) - Create `createAuthProvider()` factory - Port SSO cookie logic as opt-in - Update all 3 dashboard auth contexts - Test login/logout flows in each dashboard ### Phase 4: Design System (Week 4+) 9. **Extract `@bytelyst/design-tokens`** (P3, 4 hours) - Migrate `mindlyst.tokens.json` as canonical source - Write generation script for CSS, TS, Kotlin, Swift - Add to MindLyst build pipeline - Consider adopting for LysnrAI dashboards (future) --- ## 7. Advantages ### 7.1 Immediate Benefits | Benefit | Impact | Measurement | |---------|--------|-------------| | **~1,580 LOC eliminated** | Less code to maintain | 8 fewer files per service feature | | **Single-point security fixes** | JWT/auth vulnerability fixed once, applied everywhere | 1 PR instead of 5 | | **Consistent error handling** | All services return identical error shapes | 0 divergence | | **Faster new service creation** | `createServiceApp()` → 15 lines to launch | Minutes vs hours | | **Cosmos DB config in one place** | Container registry + TTL managed centrally | 1 file vs 6 | ### 7.2 Medium-Term Benefits | Benefit | Impact | |---------|--------| | **MindLyst backend bootstrap** | When MindLyst adds backend services, it gets Fastify infra for free | | **Consistent design language** | Token generation ensures iOS/Android/Web never drift | | **Cross-product features** | Tracker service already product-agnostic; shared auth enables MindLyst to use it | | **Onboarding speed** | New developer learns one pattern, applies to all services | | **Testability** | Shared packages have isolated tests; consumer tests focus on business logic | ### 7.3 Architectural Benefits | Benefit | Description | |---------|-------------| | **Separation of concerns** | Infrastructure (how) separated from business logic (what) | | **Dependency inversion** | Services depend on abstractions (`@bytelyst/auth`) not implementations | | **DRY principle** | No more copy-paste-modify cycle for new services | | **Contract stability** | Shared types ensure API contracts don't drift between services | | **Upgrade path** | Update `jose` or `@azure/cosmos` in one place | --- ## 8. Cautions & Risks ### 8.1 HIGH RISK — Tightly Coupled Releases | Risk | Description | Mitigation | |------|-------------|------------| | **Breaking change cascade** | A breaking change in `@bytelyst/auth` breaks all services simultaneously | Semantic versioning + `file:` references pin to exact commits. Run all consumer tests before publishing. | | **Diamond dependency** | Two packages depend on different versions of a shared dep | Use peer dependencies + strict version ranges. `pnpm` handles this well. | | **Deployment ordering** | Must deploy common-plat before consuming repos can use new features | Use git tags/releases. Consumers reference a specific version or commit. | ### 8.2 MEDIUM RISK — Development Friction | Risk | Description | Mitigation | |------|-------------|------------| | **Cross-repo PRs** | A feature spanning common-plat + LysnrAI requires 2 PRs | Use `file:../learning_ai_common_plat/packages/X` in dev, pin to version in CI. | | **Local dev setup** | Developers need all 3 repos cloned side-by-side | Document in README. Add a setup script. Consider git submodules as alternative. | | **IDE navigation** | "Go to definition" may not work across repos | TypeScript project references or workspace-level `tsconfig.json` can help. | | **Over-abstraction** | Temptation to extract things that aren't truly shared | Rule: **only extract when ≥2 consumers exist AND the code is >90% identical**. | ### 8.3 LOW RISK — Operational | Risk | Description | Mitigation | |------|-------------|------------| | **npm registry dependency** | Publishing to GitHub Packages adds infra complexity | Start with `file:` references (zero infra). Upgrade to registry only if team grows. | | **Test isolation** | Shared package tests may not catch consumer-specific issues | Each consumer repo keeps integration tests. Shared packages only unit-test their own API. | | **Python exclusion** | The desktop app and FastAPI backend can't use TypeScript packages | Python remains independent. If patterns emerge (e.g., Cosmos client), create a Python equivalent later. | | **KMP exclusion** | MindLyst's Kotlin code can't use npm packages directly | Only `design-tokens` applies to KMP (via generated Kotlin). All other packages are TS-only. | ### 8.4 Things to AVOID 1. **Don't extract too early** — Wait until code is stable and duplicated in ≥2 places 2. **Don't create a "utils" dumping ground** — Every package must have a clear, single responsibility 3. **Don't abstract configuration** — Each service's `config.ts` should remain in the service; only the base schema is shared 4. **Don't share React components** — UI components are product-specific; only infrastructure (auth, fetch) is shared 5. **Don't break existing imports in one big PR** — Migrate one package at a time, keep old imports working temporarily --- ## 9. Versioning Strategy ### Recommended: File References (Phase 1) ```jsonc // In LysnrAI's services/platform-service/package.json { "dependencies": { "@bytelyst/errors": "file:../../../learning_ai_common_plat/packages/errors", "@bytelyst/cosmos": "file:../../../learning_ai_common_plat/packages/cosmos" } } ``` **Pros:** Zero infrastructure, works locally, `pnpm install` resolves it. **Cons:** All repos must be cloned side-by-side. Docker builds need a multi-stage approach or volume mount. ### Future: GitHub Packages (Phase 2+) ```jsonc { "dependencies": { "@bytelyst/errors": "^1.0.0", "@bytelyst/cosmos": "^1.0.0" } } ``` **Pros:** True version pinning, CI-friendly, no local path dependency. **Cons:** Requires GitHub Packages setup, publish workflow, access tokens. ### Versioning Rules - **MAJOR** (1.0 → 2.0): Breaking change to public API (renamed export, removed function, changed return type) - **MINOR** (1.0 → 1.1): New export, new optional parameter, new error subclass - **PATCH** (1.0.0 → 1.0.1): Bug fix, internal refactor, dependency update --- ## 10. Testing Strategy ### Package-Level Tests Each `@bytelyst/*` package has its own `vitest` test suite: | Package | Test Focus | |---------|-----------| | errors | Error class hierarchy, statusCode correctness, instanceof checks | | cosmos | Client singleton behavior, container registry, env var reading (mocked) | | config | Zod schema validation, defaults, extension merging | | auth | JWT sign/verify round-trip, expiry, issuer validation, bcrypt hashing | | fastify-core | App factory produces correct hooks/routes, health endpoint shape | | api-client | Fetch wrapper behavior with mock server, error handling, auth injection | | react-auth | Provider renders, login/logout flows, localStorage persistence | | design-tokens | Generated output matches expected format for each platform | ### Consumer-Level Tests Each service/dashboard keeps its existing test suite. After migration: - Service tests verify that imported shared functions work correctly **in context** - No need to re-test JWT internals — that's the package's job - Focus on **integration**: "Does my route handler correctly call `extractAuth()` and get the right payload?" ### CI Matrix ``` common-plat CI: → Run all 8 package test suites → Run type-check (tsc --noEmit) → Run lint (ruff for future Python packages) LysnrAI CI (per-service): → Install common-plat packages (file: or registry) → Run service tests → Run Next.js build MindLyst CI: → Build KMP shared module → Build Next.js web → Verify generated tokens match source JSON ``` --- ## 11. CI/CD Impact ### Docker Build Changes Services currently build independently. After extraction: **Option A: Multi-stage with copy** (recommended for `file:` references) ```dockerfile # Copy common-plat packages into build context COPY ../learning_ai_common_plat/packages/errors /common/errors COPY ../learning_ai_common_plat/packages/cosmos /common/cosmos # Adjust package.json to point to /common/* ``` **Option B: Pre-publish** (recommended for registry) ```dockerfile # Common-plat packages are already on npm registry RUN npm install # resolves @bytelyst/* from GitHub Packages ``` **Option C: Monorepo build context** (if repos are merged or submoduled) ```dockerfile # Build context includes all repos COPY . /workspace WORKDIR /workspace/learning_voice_ai_agent/services/platform-service RUN npm install && npm run build ``` ### docker-compose.yml Changes The existing `docker-compose.yml` may need an additional volume mount or build context adjustment: ```yaml services: platform-service: build: context: . # May need to widen to parent directory dockerfile: services/platform-service/Dockerfile ``` ### GitHub Actions Changes - **New workflow:** `ci-common-plat.yml` — tests all shared packages on push to `learning_ai_common_plat` - **Modified workflows:** Each service CI installs common-plat packages before running tests - **Potential:** A "consumer test" job in `ci-common-plat.yml` that runs key consumer tests after package changes --- ## 12. Decision Log | Decision | Rationale | Alternatives Considered | |----------|-----------|------------------------| | **pnpm workspace monorepo** for common-plat | Simplest multi-package management, strict dep resolution | npm workspaces (less strict), Turborepo (overkill for 8 packages), Nx (too heavy) | | **`file:` references** initially | Zero infrastructure, works immediately | git submodules (complex merge), npm registry (needs setup), copy-paste (defeats purpose) | | **Factory pattern** for fastify-core, auth, react-auth | Services need slightly different config (port, issuer, user type) but identical boilerplate | Inheritance (fragile), config objects (less type-safe), template codegen (complex) | | **Peer dependencies** for heavy libs | Prevents version conflicts (e.g., two versions of `@azure/cosmos` in one service) | Direct deps (risk duplicate bundles), optional deps (too loose) | | **TypeScript-only** packages (no Python) | Python desktop/backend have less duplication and different patterns | Shared Python package (only 2 consumers, not worth the packaging complexity yet) | | **Design tokens as JSON → generated code** | JSON is the universal format; generation ensures consistency | Manual sync (error-prone), Figma API (adds dependency), CSS-only (excludes native) | | **NOT extracting React UI components** | UI is product-specific; only infrastructure is truly shared | Shared component library (premature — products have different UX) | | **NOT merging repos** | Three distinct products with different release cadences and teams | Monorepo (coupling), git submodules (merge conflicts) | --- ## Appendix A: File Counts Per Service (Before vs After) ### Before Refactor ``` services/platform-service/src/lib/ ├── config.ts (32 lines) ├── cosmos.ts (25 lines) ├── errors.ts (38 lines) ├── product-config.ts (9 lines) └── blob.ts (service-specific, stays) services/billing-service/src/lib/ ├── config.ts (34 lines) ← DUPLICATE ├── cosmos.ts (25 lines) ← DUPLICATE ├── errors.ts (41 lines) ← DUPLICATE └── product-config.ts (9 lines) ← DUPLICATE services/growth-service/src/lib/ ├── config.ts (25 lines) ← DUPLICATE ├── cosmos.ts (25 lines) ← DUPLICATE ├── errors.ts (35 lines) ← DUPLICATE └── product-config.ts (11 lines) ← DUPLICATE services/tracker-service/src/lib/ ├── auth.ts (52 lines) ← DUPLICATE ├── config.ts (24 lines) ← DUPLICATE ├── cosmos.ts (25 lines) ← DUPLICATE ├── errors.ts (44 lines) ← DUPLICATE └── product-config.ts (7 lines) ← DUPLICATE ``` ### After Refactor ``` services/platform-service/src/lib/ ├── blob.ts (service-specific, stays) └── (all others → @bytelyst/*) services/billing-service/src/lib/ └── (empty — all moved to @bytelyst/*) services/growth-service/src/lib/ └── (empty — all moved to @bytelyst/*) services/tracker-service/src/lib/ └── (empty — all moved to @bytelyst/*) Each server.ts: ~85 lines → ~15 lines Each config.ts: ~30 lines → ~5 lines (just the extension schema) ``` --- ## Appendix B: Ecosystem Metrics | Metric | Before | After | Delta | |--------|--------|-------|-------| | Duplicated infrastructure LOC | ~1,856 | ~275 | **-85%** | | Number of `errors.ts` files | 4 | 1 | **-75%** | | Number of `cosmos.ts` files | 6 | 1 | **-83%** | | Number of `auth/jwt` files | 5 | 1 | **-80%** | | Number of `server.ts` boilerplate lines | 351 | ~60 | **-83%** | | Time to create new Fastify service | ~2 hours | ~20 min | **-83%** | | Time to fix a JWT vulnerability | 5 PRs | 1 PR | **-80%** | | Design token sync effort (MindLyst) | Manual (5 files) | Automated (1 JSON) | **-100%** |