# 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<AuthPayload> { ... }
export function requireRole(req: FastifyRequest, ...roles: string[]): Promise<AuthPayload> { ... }
```

**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 = '<product>'` 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 = '<name>'` 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<TEvent extends BaseEvent>(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<string, boolean>` with `isFeatureEnabled()`, `getAllFlags()`, `setFlag()`

**Recommendation:** Create `@bytelyst/backend-flags`:

```ts
export function createFlagRegistry(defaults: Record<string, boolean>) { ... }
```

**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
