- CROSS_REPO_DRY_AUDIT.md: identified 14 duplication patterns across 9 product backends + web clients (~4,200 LOC duplicated) - CROSS_REPO_DRY_MIGRATION_ROADMAP.md: 5-phase execution plan with concrete file lists, migration steps, verification checkpoints, rollback strategy, and success metrics - Phase 0: quick wins (delete errors.ts re-exports, standardize product-config) - Phase 1: @bytelyst/fastify-auth (auth + request-context for 9 repos) - Phase 2: @bytelyst/backend-config + datastore migration for 3 older repos - Phase 3: backend-flags, backend-telemetry, domain-events (FlowMonk + ActionTrail) - Phase 4: web client DRY (telemetry, diagnostics, product-config conveniences) - Estimated: 11 days, 5 new packages, ~3,260 LOC eliminated
11 KiB
Cross-Repo DRY Audit — Migration & Integration Candidates
Date: 2026-03-20 Scope: 9 product backends + web dashboards vs. 53
@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. Found 6 high-priority and 4 medium-priority duplication patterns across backends and web clients. 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: ~2,400 lines backend + ~1,800 lines web = ~4,200 lines of duplicated code that could be replaced by shared packages.
HIGH Priority — Backend lib/ Duplication (9 repos × 6 files)
1. auth.ts — JWT verification + JWKS + role checking
Status: Identical across all 9 repos (60-90 lines each = ~700 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-50 lines each = ~350 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 switch (Cosmos vs Memory)
Status: Identical in 6/9 repos; 3 older repos use direct Cosmos without the provider abstraction
Pattern: Import @bytelyst/datastore, configure based on DB_PROVIDER env var
Recommendation: Already have @bytelyst/datastore package. The 3 older repos (LysnrAI, MindLyst, ChronoMind) should be migrated to use @bytelyst/datastore with DB_PROVIDER support instead of direct @azure/cosmos calls.
Migration needed: LysnrAI, MindLyst, ChronoMind backends
6. product-config.ts — Backend product identity
Status: Present in 4/9 repos (FlowMonk, ActionTrail, NoteLett, NomGap), hardcoded in others
Pattern: Read shared/product.json, export PRODUCT_ID, productConfig
Recommendation: Already solved by @bytelyst/config (loadProductIdentity). The 5 repos without product-config.ts should adopt @bytelyst/config/product-identity instead of hardcoding product IDs.
Migration needed: LysnrAI, MindLyst, ChronoMind, JarvisJr, 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, JarvisJr have near-identical wrappers (~35 lines each)
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, JarvisJr web apps
12. diagnostics.ts — Web diagnostics init
Status: NomGap, NoteLett, ChronoMind, JarvisJr 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 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 - FlowMonk/ActionTrail: inline object literal
- JarvisJr/ChronoMind: custom exports
Recommendation: Standardize on shared/product.json import pattern. Could add @bytelyst/web-config convenience that reads product.json and provides typed exports with NEXTPUBLIC env var overrides.
Migration Priority Matrix
| # | Candidate | LOC Saved | Repos | Effort | Priority |
|---|---|---|---|---|---|
| 1 | auth.ts → @bytelyst/fastify-auth |
~700 | 9 | 2 days | P0 |
| 2 | request-context.ts → merge into fastify-auth |
~350 | 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 | Older repos → @bytelyst/datastore |
~300 | 3 | 1 day | P1 |
| 6 | Hardcoded productId → @bytelyst/config |
~50 | 5 | 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 | 4 | 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,700+ duplicated lines
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