- 7 phases (0-6 + future): scaffolding, P0 packages, P1 packages, P2 packages, design tokens, CI/CD, verification - ~150 checkboxed tasks covering extract, test, integrate, cleanup, Docker, and docs - Dependency-ordered execution: errors → cosmos → config → auth → fastify-core → api-client → react-auth → design-tokens - Estimated ~28-41 hours total effort
25 KiB
Common Platform Extraction — Roadmap
Scope: Extract shared infrastructure from
learning_voice_ai_agent(LysnrAI) andlearning_multimodal_memory_agents(MindLyst) intolearning_ai_common_platas reusable@bytelyst/*npm packages.Companion docs: COMMON_PLATFORM_ANALYSIS.md · ECOSYSTEM_ARCHITECTURE.md · ecosystem-after-refactor.drawio
Phase 0 — Repo Scaffolding & Tooling
Goal: Initialize
learning_ai_common_platas a proper monorepo with build tooling, so packages can be developed, tested, and consumed.
- 0.1 Initialize
package.jsonat repo root ("private": true,"name": "@bytelyst/root") - 0.2 Add
pnpm-workspace.yamldeclaringpackages/*as workspace members - 0.3 Create
tsconfig.base.jsonwith shared compiler options (ESM, strict, target ES2022, module NodeNext) - 0.4 Add shared
vitest.config.tsat repo root - 0.5 Add
.gitignore(node_modules, dist, coverage, .DS_Store) - 0.6 Add
.nvmrcmatching LysnrAI's Node version - 0.7 Add
README.mdwith repo purpose, package list, and quick-start - 0.8 Add
CONTRIBUTING.mdwith conventions (commit format, PR process, testing expectations) - 0.9 Verify:
pnpm installsucceeds,pnpm -r buildsucceeds (empty packages OK)
Phase 1 — P0 Packages: Errors & Cosmos (Drop-in, No Config)
Goal: Extract the two leaf packages with zero dependencies on other
@bytelyst/*packages. These are the safest, highest-copy-count extractions.
1A — @bytelyst/errors
Extract:
- 1A.1 Create
packages/errors/package.json(name, version 0.1.0, type module, exports) - 1A.2 Create
packages/errors/tsconfig.jsonextending../../tsconfig.base.json - 1A.3 Write
packages/errors/src/service-error.ts— baseServiceErrorclass with optionaldetails - 1A.4 Write
packages/errors/src/http-errors.ts—NotFoundError,BadRequestError,UnauthorizedError,ForbiddenError,ConflictError,TooManyRequestsError - 1A.5 Write
packages/errors/src/index.ts— barrel export - 1A.6 Add build script (
tsc)
Test:
- 1A.7 Write
packages/errors/src/__tests__/errors.test.ts— verify statusCode, message, instanceof, details field - 1A.8 Run
pnpm --filter @bytelyst/errors test— all pass
Integrate into LysnrAI:
- 1A.9 Add
"@bytelyst/errors": "file:../../../learning_ai_common_plat/packages/errors"toservices/platform-service/package.json - 1A.10 Replace
import { ServiceError, ... } from "./lib/errors.js"→from "@bytelyst/errors"in platform-service - 1A.11 Run platform-service tests — all pass
- 1A.12 Repeat 1A.9–1A.11 for billing-service
- 1A.13 Repeat 1A.9–1A.11 for growth-service
- 1A.14 Repeat 1A.9–1A.11 for tracker-service
Clean up old code:
- 1A.15 Delete
services/platform-service/src/lib/errors.ts - 1A.16 Delete
services/billing-service/src/lib/errors.ts - 1A.17 Delete
services/growth-service/src/lib/errors.ts - 1A.18 Delete
services/tracker-service/src/lib/errors.ts - 1A.19 Run all 4 service test suites — confirm no regressions
- 1A.20 Verify Docker builds for all 4 services still pass
Commit: feat(errors): extract @bytelyst/errors shared package
1B — @bytelyst/cosmos
Extract:
- 1B.1 Create
packages/cosmos/package.json(peer dep:@azure/cosmos) - 1B.2 Create
packages/cosmos/tsconfig.json - 1B.3 Write
packages/cosmos/src/client.ts—getCosmosClient(),getDatabase(),getContainer() - 1B.4 Write
packages/cosmos/src/containers.ts—registerContainers(),initializeAllContainers(),ContainerConfig - 1B.5 Write
packages/cosmos/src/types.ts—ContainerConfiginterface - 1B.6 Write
packages/cosmos/src/index.ts— barrel export - 1B.7 Add build script
Test:
- 1B.8 Write
packages/cosmos/src/__tests__/client.test.ts— mock@azure/cosmos, verify singleton, env var reading - 1B.9 Write
packages/cosmos/src/__tests__/containers.test.ts— verify registry, partition key config - 1B.10 Run
pnpm --filter @bytelyst/cosmos test— all pass
Integrate into LysnrAI services (simple client):
- 1B.11 Add dep + replace imports in platform-service
src/lib/cosmos.ts - 1B.12 Run platform-service tests — all pass
- 1B.13 Repeat for billing-service
- 1B.14 Repeat for growth-service
- 1B.15 Repeat for tracker-service
Integrate into LysnrAI dashboards (client + registry):
- 1B.16 Add dep + replace imports in admin-dashboard-web
src/lib/cosmos.ts— keep container definitions asregisterContainers()call - 1B.17 Run
next buildfor admin-dashboard — passes - 1B.18 Add dep + replace imports in user-dashboard-web
src/lib/cosmos.ts - 1B.19 Run
next buildfor user-dashboard — passes
Clean up old code:
- 1B.20 Delete
services/platform-service/src/lib/cosmos.ts - 1B.21 Delete
services/billing-service/src/lib/cosmos.ts - 1B.22 Delete
services/growth-service/src/lib/cosmos.ts - 1B.23 Delete
services/tracker-service/src/lib/cosmos.ts - 1B.24 Reduce
admin-dashboard-web/src/lib/cosmos.tsto just container config +registerContainers()call - 1B.25 Reduce
user-dashboard-web/src/lib/cosmos.tsto just container config +registerContainers()call - 1B.26 Run all service tests +
next buildfor both dashboards — no regressions - 1B.27 Verify Docker builds pass
Commit: feat(cosmos): extract @bytelyst/cosmos shared package
Phase 2 — P1 Packages: Config, Auth, Fastify Core
Goal: Extract the three packages that depend on Phase 1 packages. These have higher impact but require more care due to inter-package dependencies.
2A — @bytelyst/config
Extract:
- 2A.1 Create
packages/config/package.json(peer dep:zod) - 2A.2 Write
packages/config/src/base-schema.ts—baseEnvSchema(PORT, HOST, NODE_ENV, CORS_ORIGIN, SERVICE_NAME, COSMOS_ENDPOINT, COSMOS_KEY, COSMOS_DATABASE) - 2A.3 Write
packages/config/src/loader.ts—loadConfig(extension?)function - 2A.4 Write
packages/config/src/product-identity.ts—loadProductIdentity()readsproduct.jsonor env vars - 2A.5 Write
packages/config/src/index.ts - 2A.6 Add build script
Test:
- 2A.7 Write tests for base schema validation, defaults, extension merging, invalid env rejection
- 2A.8 Write tests for product identity loading (file + env fallback)
- 2A.9 Run
pnpm --filter @bytelyst/config test— all pass
Integrate into LysnrAI:
- 2A.10 Refactor platform-service
config.ts→ importloadConfig()+ extend with service-specific fields only - 2A.11 Replace
product-config.tsimports withloadProductIdentity()in platform-service - 2A.12 Run platform-service tests — all pass
- 2A.13 Repeat for billing-service (extend with STRIPE_, BILLING_)
- 2A.14 Repeat for growth-service (extend with STRIPE_, WEBHOOK_)
- 2A.15 Repeat for tracker-service (extend with JWT_SECRET, DEFAULT_PRODUCT_ID)
Clean up old code:
- 2A.16 Delete 4×
config.ts(service-specific parts stay as extension schemas) - 2A.17 Delete 4×
product-config.ts - 2A.18 Run all service tests — no regressions
- 2A.19 Verify Docker builds
Commit: feat(config): extract @bytelyst/config shared package
2B — @bytelyst/auth
Extract:
- 2B.1 Create
packages/auth/package.json(peer deps:jose,bcryptjs, dep:@bytelyst/config) - 2B.2 Write
packages/auth/src/jwt.ts—createJwtUtils({ issuer, accessTokenExpiry, refreshTokenExpiry }) - 2B.3 Write
packages/auth/src/middleware.ts—extractAuth()Fastify hook,requireRole()guard - 2B.4 Write
packages/auth/src/server-auth.ts—getCurrentUser()for Next.js API routes - 2B.5 Write
packages/auth/src/password.ts—hashPassword(),verifyPassword() - 2B.6 Write
packages/auth/src/types.ts—AuthPayload,TokenPayloadinterfaces - 2B.7 Write
packages/auth/src/index.ts - 2B.8 Add build script
Test:
- 2B.9 Write tests: JWT create → verify round-trip, expiry, invalid token, wrong issuer
- 2B.10 Write tests: bcrypt hash → verify round-trip, wrong password rejection
- 2B.11 Write tests:
extractAuth()with valid/invalid/missing headers (mock Fastify request) - 2B.12 Run
pnpm --filter @bytelyst/auth test— all pass
Integrate into LysnrAI services:
- 2B.13 Refactor platform-service
modules/auth/jwt.ts→ usecreateJwtUtils({ issuer: "lysnrai" }) - 2B.14 Run platform-service auth tests — all pass
- 2B.15 Refactor tracker-service
lib/auth.ts→ useextractAuth()+requireRole() - 2B.16 Run tracker-service tests — all pass
Integrate into LysnrAI dashboards:
- 2B.17 Refactor admin-dashboard-web
lib/auth-server.ts→ usecreateJwtUtils()+hashPassword()+getCurrentUser() - 2B.18 Run admin-dashboard
next build— passes - 2B.19 Refactor user-dashboard-web
lib/auth-server.ts→ same as above with different issuer - 2B.20 Run user-dashboard
next build— passes
Clean up old code:
- 2B.21 Delete/reduce
platform-service/src/modules/auth/jwt.ts(keep only route handlers, not JWT logic) - 2B.22 Delete
tracker-service/src/lib/auth.ts - 2B.23 Reduce
admin-dashboard-web/src/lib/auth-server.tsto thin wrapper calling@bytelyst/auth - 2B.24 Reduce
user-dashboard-web/src/lib/auth-server.tsto thin wrapper calling@bytelyst/auth - 2B.25 CRITICAL: End-to-end test: login → get token → call authenticated endpoint → verify across all consumers
- 2B.26 Verify Docker builds
Commit: feat(auth): extract @bytelyst/auth shared package
2C — @bytelyst/fastify-core
Extract:
- 2C.1 Create
packages/fastify-core/package.json(peer deps:fastify,@fastify/cors,@fastify/swagger,fastify-metrics; deps:@bytelyst/errors,@bytelyst/config) - 2C.2 Write
packages/fastify-core/src/create-app.ts—createServiceApp({ name, version, description })factory - 2C.3 Write
packages/fastify-core/src/request-id.ts— x-request-id propagation hook - 2C.4 Write
packages/fastify-core/src/health.ts— health check route factoryhealthHandler(name, version) - 2C.5 Write
packages/fastify-core/src/error-handler.ts—ServiceError-aware error handler - 2C.6 Write
packages/fastify-core/src/start.ts—startService(app, port, host?)helper - 2C.7 Write
packages/fastify-core/src/index.ts - 2C.8 Add build script
Test:
- 2C.9 Write tests:
createServiceApp()returns Fastify instance with CORS, Swagger, metrics - 2C.10 Write tests: health endpoint returns correct shape
{ status, service, version, timestamp, requestId } - 2C.11 Write tests: error handler maps ServiceError → correct HTTP response
- 2C.12 Write tests: x-request-id is propagated or generated
- 2C.13 Run
pnpm --filter @bytelyst/fastify-core test— all pass
Integrate into LysnrAI:
- 2C.14 Refactor platform-service
server.ts→createServiceApp()+ register routes +startService() - 2C.15 Run platform-service tests — all pass
- 2C.16 Verify
/healthendpoint returns expected shape - 2C.17 Repeat for billing-service (keep internal key auth hook as service-specific)
- 2C.18 Repeat for growth-service
- 2C.19 Repeat for tracker-service
Clean up old code:
- 2C.20 Each
server.tsshould now be ~15 lines (import factory → register routes → start) - 2C.21 Run all 4 service test suites — no regressions
- 2C.22 Verify
services/monitoring/health-check.tsstill works with refactored health endpoints - 2C.23 Verify Docker builds for all 4 services
- 2C.24 Verify
docker-compose upstarts all services correctly
Commit: feat(fastify-core): extract @bytelyst/fastify-core shared package
Phase 3 — P2 Packages: API Client & React Auth
Goal: Extract dashboard-side libraries. These affect the Next.js apps and require browser-context testing.
3A — @bytelyst/api-client
Extract:
- 3A.1 Create
packages/api-client/package.json(no runtime deps) - 3A.2 Write
packages/api-client/src/client.ts—createApiClient({ baseUrl, getToken?, defaultHeaders? })→{ fetch, safeFetch } - 3A.3 Write
packages/api-client/src/types.ts—ApiResult<T>,ApiError,ApiClientConfig - 3A.4 Write
packages/api-client/src/index.ts - 3A.5 Add build script
Test:
- 3A.6 Write tests:
fetch<T>()calls correct URL, passes headers, throws on error - 3A.7 Write tests:
safeFetch<T>()returns{ data, error }tuple, never throws - 3A.8 Write tests: auth token injection via
getToken()callback - 3A.9 Run
pnpm --filter @bytelyst/api-client test— all pass
Integrate into LysnrAI dashboards:
- 3A.10 Refactor admin-dashboard-web
src/lib/api.ts— usecreateApiClient(), keep domain methods (apiLogin, apiListUsers, etc.) - 3A.11 Run admin-dashboard
next build— passes - 3A.12 Refactor user-dashboard-web client files (
platform-client.ts,billing-client.ts,growth-client.ts) - 3A.13 Run user-dashboard
next build— passes - 3A.14 Refactor tracker-dashboard-web
src/lib/tracker-client.ts - 3A.15 Run tracker-dashboard
next build— passes
Clean up old code:
- 3A.16 Remove duplicated
apiFetch/requestbase functions from each dashboard (domain methods stay) - 3A.17 Run all 3 dashboard builds — no regressions
Commit: feat(api-client): extract @bytelyst/api-client shared package
3B — @bytelyst/react-auth
Extract:
- 3B.1 Create
packages/react-auth/package.json(peer deps:react, dep:@bytelyst/api-client) - 3B.2 Write
packages/react-auth/src/auth-context.tsx—createAuthProvider<TUser>({ storagePrefix, loginEndpoint, ... }) - 3B.3 Write
packages/react-auth/src/use-auth.ts— typeduseAuth()hook - 3B.4 Write
packages/react-auth/src/types.ts—BaseUser,AuthContextValue<TUser>,AuthConfig - 3B.5 Write
packages/react-auth/src/index.ts - 3B.6 Add build script
Test:
- 3B.7 Write tests: AuthProvider renders children, login/logout flows, localStorage persistence
- 3B.8 Write tests: useAuth() returns correct state after login/logout
- 3B.9 Run
pnpm --filter @bytelyst/react-auth test— all pass
Integrate into LysnrAI dashboards:
- 3B.10 Refactor admin-dashboard-web
src/lib/auth-context.tsx→createAuthProvider<AdminUser>({ storagePrefix: "admin", ... }) - 3B.11 Run admin-dashboard
next build— passes - 3B.12 Test login/logout flow manually in admin dashboard
- 3B.13 Refactor user-dashboard-web
src/lib/auth-context.tsx→createAuthProvider<PortalUser>({ storagePrefix: "portal", enableSSO: true, ... }) - 3B.14 Run user-dashboard
next build— passes - 3B.15 Test login/logout + SSO flow manually in user dashboard
- 3B.16 Refactor tracker-dashboard-web auth context →
createAuthProvider<TrackerUser>({ storagePrefix: "tracker", ... }) - 3B.17 Run tracker-dashboard build — passes
Clean up old code:
- 3B.18 Delete
admin-dashboard-web/src/lib/auth-context.tsx(replaced by imported provider) - 3B.19 Delete
user-dashboard-web/src/lib/auth-context.tsx - 3B.20 Reduce tracker-dashboard auth context to thin config file
- 3B.21 Run all 3 dashboard builds — no regressions
- 3B.22 CRITICAL: Test all login flows end-to-end across all 3 dashboards
Commit: feat(react-auth): extract @bytelyst/react-auth shared package
Phase 4 — P3 Package: Design Tokens
Goal: Create a cross-platform design token pipeline that generates CSS, TypeScript, Kotlin, and Swift from a single JSON source.
Extract:
- 4.1 Create
packages/design-tokens/package.json - 4.2 Migrate
mindlyst.tokens.json→packages/design-tokens/tokens/bytelyst.tokens.jsonas canonical source - 4.3 Write
packages/design-tokens/scripts/generate.ts— reads JSON, outputs 4 formats - 4.4 Generate
packages/design-tokens/generated/tokens.css(CSS custom properties--ml-*) - 4.5 Generate
packages/design-tokens/generated/tokens.ts(TypeScript constants) - 4.6 Generate
packages/design-tokens/generated/MindLystTokens.kt(Kotlin object) - 4.7 Generate
packages/design-tokens/generated/MindLystTheme.swift(Swift structs) - 4.8 Add
generatescript to package.json:tsx scripts/generate.ts
Test:
- 4.9 Write tests: generated CSS contains all expected
--ml-*properties - 4.10 Write tests: generated TS exports match JSON keys
- 4.11 Write tests: generated Kotlin compiles (syntax check)
- 4.12 Write tests: generated Swift compiles (syntax check)
- 4.13 Run
pnpm --filter @bytelyst/design-tokens test— all pass
Integrate into MindLyst:
- 4.14 Replace
mindlyst-native/shared/.../theme/MindLystTokens.ktwith generated version - 4.15 Verify KMP build:
./gradlew :shared:compileKotlinIosSimulatorArm64 - 4.16 Replace
iosApp/MindLystTheme.swiftwith generated version - 4.17 Replace
web/src/styles/globals.csstoken section with import of generated CSS - 4.18 Replace
design-system/web/mindlyst.csswith generated CSS - 4.19 Run
cd web && npx next build— passes
Clean up old code:
- 4.20 Remove manually-maintained token values from MindLyst (now auto-generated)
- 4.21 Update MindLyst
CONTRIBUTING.mdwith token update workflow (edit JSON → run generate → commit) - 4.22 Verify visual consistency: spot-check colors in iOS preview and web dev server
Commit: feat(design-tokens): extract @bytelyst/design-tokens with cross-platform generation
Phase 5 — CI/CD & Docker
Goal: Ensure the shared packages work correctly in CI, Docker builds, and deployment pipelines.
CI Setup
- 5.1 Create
.github/workflows/ci-common-plat.yml— test all 8 packages on push - 5.2 Add matrix strategy: Node 18 + Node 20
- 5.3 Add type-check step (
pnpm -r exec tsc --noEmit) - 5.4 Add lint step (if eslint/biome is added)
Docker Build Compatibility
- 5.5 Update platform-service
Dockerfileto copy common-plat packages into build context - 5.6 Update billing-service
Dockerfile - 5.7 Update growth-service
Dockerfile - 5.8 Update tracker-service
Dockerfile - 5.9 Update admin-dashboard-web
Dockerfile - 5.10 Update user-dashboard-web
Dockerfile - 5.11 Update tracker-dashboard-web
Dockerfile(if exists) - 5.12 Update
docker-compose.ymlbuild contexts if needed (may need to widen to parent dir) - 5.13 Run
docker compose build— all images build successfully - 5.14 Run
docker compose up -d— all services start and pass health checks
Consumer CI Updates
- 5.15 Update
ci-platform-service.yml— install common-plat deps before test - 5.16 Update
ci-billing-service.yml - 5.17 Update
ci-growth-service.yml - 5.18 Update
ci-tracker-service.yml - 5.19 Update dashboard CI workflows (if they exist)
Commit: chore(ci): add common-plat CI and update consumer Docker builds
Phase 6 — Final Verification & Documentation
Goal: Comprehensive end-to-end validation, documentation update, and cleanup.
Full Regression Test
- 6.1 Run all Python tests:
python -m pytest tests/ backend/tests/ -v --tb=short - 6.2 Run all platform-service tests:
cd services/platform-service && npm test - 6.3 Run all billing-service tests:
cd services/billing-service && npm test - 6.4 Run all growth-service tests:
cd services/growth-service && npm test - 6.5 Run all tracker-service tests:
cd services/tracker-service && npm test - 6.6 Build admin-dashboard:
cd admin-dashboard-web && npx next build - 6.7 Build user-dashboard:
cd user-dashboard-web && npx next build - 6.8 Build tracker-dashboard:
cd tracker-dashboard-web && npx next build - 6.9 Build MindLyst KMP:
./gradlew :shared:compileKotlinIosSimulatorArm64 - 6.10 Build MindLyst web:
cd web && npx next build - 6.11 Run common-plat tests:
pnpm -r test - 6.12 Docker compose full stack:
docker compose up -d→ all healthy
End-to-End Flow Tests
- 6.13 Test: Admin login → create user → view user list
- 6.14 Test: User login → view profile → update settings
- 6.15 Test: Tracker login → create item → add comment → vote
- 6.16 Test: Public roadmap page → vote → submit idea
- 6.17 Test: Service health checks via monitoring script
Documentation Updates
- 6.18 Update LysnrAI
AGENTS.md— add@bytelyst/*packages to file ownership map - 6.19 Update LysnrAI
README_MONO_REPO.md— document common-plat dependency - 6.20 Update LysnrAI
docs/API_REFERENCE.md— note shared auth/errors - 6.21 Update MindLyst
AGENTS.md— reference design-tokens package - 6.22 Update common-plat
README.md— package API reference, usage examples - 6.23 Write
docs/MIGRATION_GUIDE.md— step-by-step for adopting@bytelyst/*in a new project - 6.24 Update this
ROADMAP.md— mark all tasks as complete
Code Cleanup
- 6.25 Search LysnrAI for any remaining duplicated
cosmos.ts/errors.ts/auth.tsfiles - 6.26 Remove any dead imports or unused dependencies from service
package.jsonfiles - 6.27 Run
pnpm -r exec depcheckto find unused deps (if depcheck installed) - 6.28 Final git status check: all repos clean, no untracked artifacts
Commit: docs: update all documentation for common platform integration
Phase 7 — Future Enhancements (Post-MVP)
Goal: Deferred improvements that add value but aren't required for initial extraction.
- 7.1 Publish
@bytelyst/*packages to GitHub Packages (private npm registry) - 7.2 Add Changesets for automated version management and changelogs
- 7.3 Create reusable GitHub Actions workflow templates for service CI
- 7.4 Add
@bytelyst/blobpackage (extract blob storage client + SAS generation) - 7.5 Add
@bytelyst/monitoringpackage (health check aggregator) - 7.6 Add
@bytelyst/testingpackage (shared test utilities, mock factories) - 7.7 Evaluate Python shared package for
cosmos_client.py+blob_client.pyif MindLyst adds Python backend - 7.8 Integrate
@bytelyst/design-tokensinto LysnrAI dashboards (unified design language) - 7.9 Add pre-commit hooks to auto-run token generation when JSON changes
- 7.10 Set up Renovate/Dependabot for common-plat dependency updates
Summary
| Phase | Packages | Est. Effort | Risk Level |
|---|---|---|---|
| 0 | Repo scaffolding | 1–2 hours | None |
| 1 | errors + cosmos |
4–6 hours | Low |
| 2 | config + auth + fastify-core |
8–12 hours | Medium |
| 3 | api-client + react-auth |
5–7 hours | Medium |
| 4 | design-tokens |
4–6 hours | Low |
| 5 | CI/CD + Docker | 3–4 hours | Medium |
| 6 | Verification + docs | 3–4 hours | Low |
| 7 | Future enhancements | Ongoing | — |
| Total | 8 packages | ~28–41 hours |
Execution Order
Phase 0 → Phase 1A → Phase 1B → Phase 2A → Phase 2B → Phase 2C → Phase 3A → Phase 3B → Phase 4 → Phase 5 → Phase 6
↑ ↑
depends on 1A/1B depends on 2A
Rule: Never start a phase until the previous phase's tests and Docker builds pass. Each phase ends with a commit and all-green test suite.