Verified claims against actual codebase. Key corrections: Audit (CROSS_REPO_DRY_AUDIT.md): - Fix #5: all 9 repos already use @bytelyst/datastore — issue is only DB_PROVIDER missing from config.ts in 3 older repos (not a full migration) - Fix #6: only 3 repos have product-config.ts (not 4) — NomGap was wrong - Fix #11: web telemetry.ts is in NomGap+NoteLett+ChronoMind+LysnrAI (not JarvisJr) - Fix #12: web diagnostics.ts includes LysnrAI user-dashboard-web (5 repos total) - Fix auth.ts LOC: exactly 79 lines × 9 repos = 711 (was '60-90') - Fix request-context.ts LOC: 30-49 lines range (was '~30-50') - Fix package count: 50 packages (not 53) - Add items 15-16: web auth.ts + billing-client.ts (noted as keep-as-is) - Fix LOC math: ~2,700 total (was inflated ~4,200) - Add cosmos-init.ts note (5 repos, product-specific, not consolidation candidates) Roadmap (CROSS_REPO_DRY_MIGRATION_ROADMAP.md): - Phase 0.2: 6 repos need product-config.ts (add NomGap) - Phase 2.3: rewritten from 'migrate to datastore' to 'add DB_PROVIDER to config' - Phase 4: add LysnrAI user-dashboard-web to affected repos, remove JarvisJr from telemetry - Fix product-config.ts template path (was ../../../../, now ../../../ with depth note) - Fix success metrics: packages 50→55, product-config 3→9, LOC ~1,760 - Fix overview table: Phase 2 name, Phase 4 repo count
13 KiB
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:
// 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:
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, NODEENV, 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:
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:
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:
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:
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/configloadProductIdentity()(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)
- Create
@bytelyst/fastify-authpackage withextractAuth(),requireRole(),createRequestContext() - Migrate all 9 backends to use it (delete local
auth.ts+request-context.ts) - Delete all
errors.tsre-export files, update imports to@bytelyst/errorsdirectly
Sprint 2 (P1 — 3.5 days)
- Create
@bytelyst/backend-configwith composable Zod schema factory - Migrate LysnrAI, MindLyst, ChronoMind backends to
@bytelyst/datastore - Migrate 5 repos to
@bytelyst/config/product-identityfor productId
Sprint 3 (P2 — 4 days)
- Create
@bytelyst/domain-eventsfactory (from FlowMonk/ActionTrail pattern) - Create
@bytelyst/backend-flagsand@bytelyst/backend-telemetry - Add convenience functions to web-side shared packages
- Standardize web
product-config.tspattern