# Cross-Repo DRY Audit — Migration & Integration Candidates > **Date:** 2026-03-20 > **Scope:** 9 product backends + web dashboards vs. 50 `@bytelyst/*` shared packages > **Goal:** Identify duplicated patterns across product repos that should be consolidated into common platform packages. --- ## Executive Summary Audited all 9 product repos with Fastify backends + 5 Next.js web dashboards. Found **6 high-priority** and **4 medium-priority** backend duplication patterns, plus **6 web-side** duplication patterns. The most impactful candidates involve backend `lib/` files that are **copy-pasted identically** (or near-identically) across all repos — differing only by product ID, port number, or service name. **Estimated LOC savings:** ~1,700 lines backend + ~1,000 lines web = **~2,700 lines** of duplicated code that could be replaced by shared packages or eliminated entirely. --- ## HIGH Priority — Backend `lib/` Duplication (9 repos × 6 files) ### 1. `auth.ts` — JWT verification + JWKS + role checking **Status:** Identical across all 9 repos (79 lines each = 711 LOC total) **Pattern:** RS256 JWKS verification → HS256 fallback → `extractAuth()` + `requireRole()` **Only difference:** imports `config` from local `./config.js` **Recommendation:** Create `@bytelyst/fastify-auth` package: ```ts // New package: packages/fastify-auth/ export function createAuthPlugin(opts: { jwtSecret: string; jwksUrl?: string }) { ... } export function extractAuth(req: FastifyRequest): Promise { ... } export function requireRole(req: FastifyRequest, ...roles: string[]): Promise { ... } ``` **Affected repos:** LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, FlowMonk, NoteLett, ActionTrail --- ### 2. `request-context.ts` — productId validation + getUserId() **Status:** Nearly identical across all 9 repos (30-49 lines each = ~315 LOC total) **Only difference:** `const PRODUCT_ID = ''` string literal **Recommendation:** Add to `@bytelyst/fastify-auth` or new `@bytelyst/request-context`: ```ts export function createRequestContext(productId: string) { return { getRequestProductId(req: FastifyRequest): string { ... }, getUserId(req: FastifyRequest): string { ... }, }; } ``` **Affected repos:** All 9 backends --- ### 3. `errors.ts` — Re-export of `@bytelyst/errors` **Status:** Identical in 8/9 repos (FlowMonk imports directly) **Pattern:** 7-12 line file that just re-exports from `@bytelyst/errors` **Recommendation:** **DELETE these files.** Product repos should import `@bytelyst/errors` directly in their modules — the re-export layer adds no value. **Affected repos:** LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, NoteLett, ActionTrail --- ### 4. `config.ts` — Zod env schema **Status:** 80% identical across all 9 repos (~15-30 lines each = ~200 LOC total) **Common fields:** PORT, HOST, NODE*ENV, CORS_ORIGIN, SERVICE_NAME, DB_PROVIDER, COSMOS*\*, JWT_SECRET, PLATFORM_JWKS_URL **Product-specific fields:** PLATFORM_SERVICE_URL, EXTRACTION_SERVICE_URL, WEBHOOK_SECRET, LLM_API_KEY, etc. **Recommendation:** Create `@bytelyst/backend-config` with a composable schema: ```ts import { createBackendConfig } from '@bytelyst/backend-config'; export const config = createBackendConfig({ serviceName: 'nomgap-backend', defaultPort: 4013, extraSchema: z.object({ EXTRACTION_SERVICE_URL: z.string().default('http://localhost:4005'), }), }); ``` **Affected repos:** All 9 backends --- ### 5. `datastore.ts` — DB_PROVIDER in config vs. hardcoded env read **Status:** All 9 repos already use `@bytelyst/datastore` (CosmosDatastoreProvider / MemoryDatastoreProvider). However, 3 older repos (LysnrAI, MindLyst, ChronoMind) read `DB_PROVIDER` directly from `process.env` inside `datastore.ts` instead of declaring it in their Zod `config.ts` schema. The 6 newer repos properly declare `DB_PROVIDER` in config. **Pattern:** Import `@bytelyst/datastore`, configure based on `DB_PROVIDER` env var **Recommendation:** Add `DB_PROVIDER` to the Zod config schema in the 3 older repos so it's validated at startup like other env vars. This is a minor cleanup (not a full migration). **Cleanup needed:** LysnrAI, MindLyst, ChronoMind — add `DB_PROVIDER` to `config.ts` **Note:** 5 repos also have a supplementary `cosmos-init.ts` file (MindLyst, ChronoMind, JarvisJr, FlowMonk, NoteLett) that registers Cosmos containers. These are product-specific and NOT candidates for consolidation — each repo registers different containers. --- ### 6. `product-config.ts` — Backend product identity **Status:** Present in 3/9 repos (FlowMonk, ActionTrail, NoteLett). The other 6 repos hardcode `const PRODUCT_ID = ''` in `request-context.ts`. **Pattern:** Read `shared/product.json`, export `PRODUCT_ID`, `productConfig` **Recommendation:** The 6 repos without `product-config.ts` should add one that reads from `shared/product.json` (all repos have this file). This eliminates hardcoded product IDs and ensures consistency. **Migration needed:** LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse --- ## MEDIUM Priority — Backend Patterns (2-3 repos) ### 7. `events.ts` — Domain event bus + SSE + webhook dispatch **Status:** FlowMonk and ActionTrail have near-identical event bus infrastructure (~150 lines each) **Pattern:** `@bytelyst/event-store` + `@bytelyst/fastify-sse` + `@bytelyst/webhook-dispatch` wired together with typed domain events + SSE hub + webhook targets **Recommendation:** Create `@bytelyst/domain-events` factory: ```ts export function createDomainEventBus(opts: { productId: string; eventTypes: TEvent['type'][]; webhookSecret?: string; }) { ... } ``` Product repos would only define their event type interfaces and call `createDomainEventBus()`. **Affected repos:** FlowMonk, ActionTrail (and future products that need real-time events) --- ### 8. `feature-flags.ts` — Backend in-memory flag registry **Status:** FlowMonk and ActionTrail have **identical** implementations (25 lines each) **Pattern:** `Map` with `isFeatureEnabled()`, `getAllFlags()`, `setFlag()` **Recommendation:** Create `@bytelyst/backend-flags`: ```ts export function createFlagRegistry(defaults: Record) { ... } ``` **Affected repos:** FlowMonk, ActionTrail (expandable to all backends) --- ### 9. `telemetry.ts` — Backend telemetry buffer **Status:** FlowMonk and ActionTrail have **byte-for-byte identical** implementations (33 lines each) **Pattern:** In-memory buffer with `trackEvent()`, `getBufferedEvents()`, `flushEvents()` **Recommendation:** Add to existing `@bytelyst/events` or new `@bytelyst/backend-telemetry`: ```ts export function createTelemetryBuffer(opts: { enabled: boolean }) { ... } ``` **Affected repos:** FlowMonk, ActionTrail --- ### 10. `scheduler.ts` — In-process job scheduler **Status:** ActionTrail has a full scheduler; FlowMonk has a scheduling engine **Pattern:** Cron parsing, job registry, runner with diagnostics **Recommendation:** ActionTrail's scheduler could generalize to `@bytelyst/cron-scheduler` for any product needing periodic jobs. Currently `@bytelyst/queue` exists but is a different pattern (durable job queue). **Affected repos:** ActionTrail (FlowMonk's scheduler is domain-specific — planning, not cron) --- ## HIGH Priority — Web `lib/` Duplication (5+ repos) ### 11. `telemetry.ts` — Web telemetry init **Status:** NomGap, NoteLett, ChronoMind, LysnrAI (user-dashboard-web) have near-identical wrappers (~35 lines each). JarvisJr does NOT have this file. **Only difference:** `channel` name string **Recommendation:** The `@bytelyst/telemetry-client` package already provides `createTelemetryClient()`. Products should call it directly in their `providers.tsx` instead of maintaining a wrapper file. Alternatively, add a `createWebTelemetry(productId, channel)` convenience to the package. **Affected repos:** NomGap, NoteLett, ChronoMind, LysnrAI web apps --- ### 12. `diagnostics.ts` — Web diagnostics init **Status:** NomGap, NoteLett, ChronoMind, JarvisJr, LysnrAI (user-dashboard-web) have near-identical wrappers (~40 lines each) **Only difference:** `channel` name, auth token retrieval method **Recommendation:** Add `createWebDiagnostics(opts)` convenience to `@bytelyst/diagnostics-client` that handles install ID generation, localStorage auth token, and default config. **Affected repos:** NomGap, NoteLett, ChronoMind, JarvisJr, LysnrAI web apps --- ### 13. `feature-flags.ts` + `kill-switch.ts` — Web client wrappers **Status:** NomGap and NoteLett have identical wrappers; ChronoMind has similar **Recommendation:** These are thin wrappers around `@bytelyst/feature-flag-client` and `@bytelyst/kill-switch-client`. Consider adding React hook convenience exports directly to the packages. --- ### 14. `product-config.ts` — Web product identity **Status:** 5 different patterns across repos: - NomGap/NoteLett: import `shared/product.json` + env var overrides (best pattern) - FlowMonk/ActionTrail: inline object literal - JarvisJr: custom `getPlatformBaseURL()` function - ChronoMind: product config embedded in `auth-api.ts` (`PRODUCT_ID` + `getBaseUrl()`) - LysnrAI user-dashboard: uses `@bytelyst/config` `loadProductIdentity()` (slightly different) **Recommendation:** Standardize on `shared/product.json` import pattern (NomGap/NoteLett style). This reads the canonical `shared/product.json` and provides typed exports with `NEXT_PUBLIC_*` env var overrides. --- ### 15. `auth.ts` — Web auth provider wrapper **Status:** NomGap and NoteLett have near-identical `auth.ts` files (~40 lines each) wrapping `createAuthProvider()` from `@bytelyst/react-auth` with product-specific types. **Only difference:** LoginResponse type shape varies slightly. **Recommendation:** Minor duplication — keep as-is since each product may have different user types. Not worth a shared package. **Affected repos:** NomGap, NoteLett (ChronoMind uses `@bytelyst/auth-client` instead) --- ### 16. `billing-client.ts` — Web subscription/billing wrapper **Status:** NomGap and ChronoMind wrap `@bytelyst/subscription-client`; LysnrAI wraps `@bytelyst/api-client`. Three different approaches to the same problem. **Recommendation:** Minor duplication — keep as-is since each product has different billing UI needs. The shared `@bytelyst/subscription-client` already handles the heavy lifting. --- ## Migration Priority Matrix | # | Candidate | LOC Saved | Repos | Effort | Priority | | --- | -------------------------------------------------------- | --------- | ----- | -------- | -------- | | 1 | `auth.ts` → `@bytelyst/fastify-auth` | ~711 | 9 | 2 days | **P0** | | 2 | `request-context.ts` → merge into fastify-auth | ~315 | 9 | 1 day | **P0** | | 3 | `errors.ts` → delete (import directly) | ~100 | 8 | 0.5 day | **P0** | | 4 | `config.ts` → `@bytelyst/backend-config` | ~200 | 9 | 2 days | **P1** | | 5 | `DB_PROVIDER` cleanup in older config.ts | ~15 | 3 | 0.5 day | **P1** | | 6 | Hardcoded productId → `product-config.ts` | ~50 | 6 | 0.5 day | **P1** | | 7 | `events.ts` → `@bytelyst/domain-events` | ~300 | 2 | 1.5 days | **P2** | | 8 | `feature-flags.ts` → `@bytelyst/backend-flags` | ~50 | 2 | 0.5 day | **P2** | | 9 | `telemetry.ts` → `@bytelyst/backend-telemetry` | ~70 | 2 | 0.5 day | **P2** | | 10 | Web telemetry/diagnostics wrappers → package convenience | ~400 | 5 | 1 day | **P2** | | 11 | Web `product-config.ts` → standardize pattern | ~200 | 5+ | 1 day | **P2** | **Total estimated effort: ~11 days for full DRY migration** **Total LOC eliminated: ~2,435+ duplicated lines** **Note:** Items 15-16 (web auth.ts, billing-client.ts) are intentionally NOT in this matrix — the duplication is minor and each product legitimately needs different types/behavior. --- ## Repos NOT Yet Using Common Platform ### `learning_ai_local_memory_gpt` - Standalone Express + SQLite app — no Cosmos, no auth, no platform-service - **No integration needed** — this is intentionally a standalone local tool ### `learning_ai_productivity_web` - No backend directory found - Needs investigation to determine if it should adopt platform patterns --- ## Recommended Execution Order ### Sprint 1 (P0 — 3.5 days) 1. Create `@bytelyst/fastify-auth` package with `extractAuth()`, `requireRole()`, `createRequestContext()` 2. Migrate all 9 backends to use it (delete local `auth.ts` + `request-context.ts`) 3. Delete all `errors.ts` re-export files, update imports to `@bytelyst/errors` directly ### Sprint 2 (P1 — 3.5 days) 4. Create `@bytelyst/backend-config` with composable Zod schema factory 5. Migrate LysnrAI, MindLyst, ChronoMind backends to `@bytelyst/datastore` 6. Migrate 5 repos to `@bytelyst/config/product-identity` for productId ### Sprint 3 (P2 — 4 days) 7. Create `@bytelyst/domain-events` factory (from FlowMonk/ActionTrail pattern) 8. Create `@bytelyst/backend-flags` and `@bytelyst/backend-telemetry` 9. Add convenience functions to web-side shared packages 10. Standardize web `product-config.ts` pattern