Status: Accepted Date: 2026-01-10 Authors: Development Team Reviewers: Architecture Team Supersedes: N/A Superseded by: N/A
Context and Problem Statement#
The Oshun monorepo consolidation requires a unified package manager strategy. Currently, the two primary codebases use different package managers:
Lilith (Current State):
- Uses Yarn workspaces (yarn.lock present)
- Also has package-lock.json (npm artifacts)
- Workspace configuration via
workspacesarray in package.json - No pnpm configuration
Yemaya (Current State):
- Uses pnpm 10.25.0 (specified via
packageManagerfield) - Has mature pnpm-workspace.yaml with dependency catalog
- Well-configured .npmrc with hoisting rules
- Uses Turbo for build orchestration (being replaced by Nx)
We need to select a single package manager for the unified Oshun monorepo that:
- Supports efficient monorepo workflows at scale
- Integrates well with Nx workspace tooling
- Provides consistent dependency resolution across all packages
- Minimizes disk space usage and install times
- Offers strong security and dependency isolation
Decision Drivers#
- Monorepo Optimization: Native workspace support with efficient linking and dependency hoisting
- Disk Efficiency: Storage optimization for large repositories with many packages
- Install Performance: Fast dependency resolution and installation
- Dependency Strictness: Prevention of phantom dependencies and undeclared imports
- Nx Integration: Seamless integration with Nx workspace features
- Developer Experience: Familiar commands, good error messages, IDE support
- Existing Investment: Leverage existing pnpm configuration from Yemaya
- Lock File Stability: Deterministic builds with minimal lockfile churn
- Security: Supply chain security features and audit capabilities
Considered Options#
Option 1: pnpm (Recommended)#
Description: Performant npm - a fast, disk space efficient package manager with strict dependency isolation and native workspace support.
Pros:
- ✅ Disk Efficiency: Content-addressable storage shares packages globally; ~60-70% disk savings vs npm
- ✅ Strict by Default: Prevents phantom dependencies - packages can only import declared dependencies
- ✅ Native Workspaces: First-class monorepo support with
pnpm-workspace.yaml - ✅ Dependency Catalog: Centralized version management via
catalog:prefix - ✅ Fast Installs: Typically 2-3x faster than npm for cold installs
- ✅ Existing Configuration: Yemaya already has mature pnpm setup to build upon
- ✅ Excellent Nx Integration: Nx has first-class pnpm support
- ✅ Symlink Strategy: Proper node_modules structure with symlinks for correct resolution
- ✅ Lockfile Stability: Minimal lockfile changes when adding/removing packages
- ✅ Active Development: Rapid feature development, responsive maintainers
Cons:
- ❌ Learning Curve: Developers unfamiliar with pnpm need onboarding
- ❌ Compatibility Issues: Some packages with hardcoded node_modules assumptions may break
- ❌ Migration Effort: Lilith packages need conversion from Yarn workspaces
- ❌ Symlink Complexity: Debugging can be harder due to symlinked structure
Cost: Low - pnpm is free, migration is straightforward Risk: Low - mature, widely adopted, well-documented Effort: 2-3 days for full migration and verification
Option 2: Yarn (Classic or Berry)#
Description: Facebook's package manager with workspaces support. Yarn Berry (v3+) introduces Plug'n'Play (PnP) for zero-install workflows.
Pros:
- ✅ Lilith Familiarity: Lilith already uses Yarn workspaces
- ✅ Zero-Install (Berry): Commit dependencies to repo for instant installs
- ✅ Stable and Mature: Long track record, large community
- ✅ Good Workspace Support: Native workspaces with selective versioning
Cons:
- ❌ Disk Inefficiency (Classic): Each project has full node_modules copy
- ❌ PnP Compatibility Issues (Berry): Many packages incompatible with Plug'n'Play
- ❌ No Dependency Catalog: Manual version synchronization across packages
- ❌ Slower than pnpm: Install times 30-50% slower for large monorepos
- ❌ Version Fragmentation: Classic vs Berry creates confusion
- ❌ Would Discard Yemaya Config: Existing pnpm setup would be abandoned
Cost: Low - Yarn is free Risk: Medium - PnP compatibility issues if using Berry Effort: 3-4 days (need to convert Yemaya from pnpm)
Option 3: npm Workspaces#
Description: Native package manager bundled with Node.js with workspace support added in npm v7+.
Pros:
- ✅ Universal Availability: Bundled with Node.js, no additional installation
- ✅ No Migration: Works with existing package-lock.json from Lilith
- ✅ Familiar Commands: Most developers know npm commands
- ✅ Improving Performance: npm v10+ significantly faster than earlier versions
Cons:
- ❌ Disk Inefficient: Full node_modules copy for each package, high disk usage
- ❌ Slower Installs: 2-3x slower than pnpm for monorepo installs
- ❌ No Dependency Catalog: Manual version management across packages
- ❌ Phantom Dependencies: Allows importing undeclared packages (hoisting side effect)
- ❌ Lockfile Churn: More frequent unnecessary changes to package-lock.json
- ❌ Would Discard Yemaya Config: Existing pnpm setup would be abandoned
- ❌ Limited Monorepo Features: Workspaces are basic compared to pnpm/Yarn
Cost: Free Risk: Medium - performance and phantom dependency issues at scale Effort: 3-4 days (need to convert Yemaya from pnpm)
Option 4: Bun#
Description: All-in-one JavaScript runtime with built-in package manager, bundler, and test runner.
Pros:
- ✅ Extremely Fast: 10-100x faster installs than npm in benchmarks
- ✅ All-in-One: Package manager, runtime, bundler, test runner
- ✅ npm Compatible: Uses existing package.json and node_modules structure
- ✅ Native Workspaces: Supports workspace protocol
Cons:
- ❌ Immature: Version 1.0 released recently, still stabilizing
- ❌ Compatibility Issues: Not all Node.js packages work correctly
- ❌ Limited Monorepo Features: No dependency catalog, basic workspace support
- ❌ Nx Integration Uncertain: Not officially supported by Nx
- ❌ Production Risk: Too new for critical production workloads
- ❌ Team Unfamiliarity: No existing experience with Bun
Cost: Free Risk: High - immature ecosystem, compatibility issues Effort: 5+ days (extensive testing required)
Decision Outcome#
Chosen option: Option 1 - pnpm
Justification:
pnpm is the optimal choice for the Oshun monorepo for the following reasons:
-
Existing Foundation: Yemaya already has a mature, well-configured pnpm setup including:
- Comprehensive pnpm-workspace.yaml with dependency catalog
- Optimized .npmrc configuration with proper hoisting rules
- Established patterns for workspace-internal dependencies
-
Monorepo Excellence: pnpm's design philosophy aligns perfectly with Nx monorepo patterns:
- Content-addressable storage dramatically reduces disk usage
- Strict dependency isolation prevents phantom dependency bugs
- Dependency catalog feature enables centralized version management
- Fast installs improve CI/CD pipeline performance
-
Nx Integration: Nx has excellent pnpm support:
nx affectedworks correctly with pnpm workspace structure- pnpm's deterministic installs complement Nx computation caching
- Shared configuration patterns between Nx and pnpm
-
Performance at Scale: For a monorepo with 50+ packages:
- Install times: pnpm ~30s vs npm ~90s vs Yarn ~60s (typical)
- Disk usage: pnpm ~500MB vs npm ~1.5GB (typical)
- CI/CD: Faster pipelines due to efficient caching
-
Developer Safety: pnpm's strictness catches errors early:
- Cannot import packages not declared in package.json
- Clear error messages for missing dependencies
- Reproducible builds across environments
Trade-offs Accepted:
- Lilith developers need brief pnpm onboarding
- Some packages may need compatibility fixes for pnpm's strict mode
- Minor workflow adjustments for developers used to npm/yarn
Implementation Plan:
-
Phase 1: Configuration Setup (Day 1)
- Create root pnpm-workspace.yaml incorporating all workspace paths
- Create root .npmrc with optimized settings
- Define comprehensive dependency catalog for shared versions
-
Phase 2: Lilith Migration (Day 1-2)
- Remove yarn.lock and package-lock.json
- Update package.json workspace configuration
- Convert
workspace:*protocol usage - Fix any hardcoded node_modules paths
-
Phase 3: Unified Install (Day 2)
- Remove all node_modules directories
- Run
pnpm installat root - Resolve any peer dependency warnings
- Verify all packages resolve correctly
-
Phase 4: Verification (Day 2-3)
- Run builds for all packages
- Run test suites
- Verify Nx commands work correctly
- Update CI/CD pipelines to use pnpm
Success Metrics:
- All packages install successfully with
pnpm install - No phantom dependency errors during build
- CI/CD install time < 60 seconds (with cache)
- Disk usage < 1GB for full node_modules
- All existing tests pass
Review Schedule: 30 days post-migration
Implementation Details#
Technical Specifications#
Root pnpm-workspace.yaml Structure:
packages:
# Oshun-level shared packages
- 'libs/**/*'
- 'apps/**/*'
- 'tools/*'
# Legacy Lilith packages (during migration)
- 'lilith/packages/*'
- 'lilith/services/*'
- 'lilith/sdk/typescript'
# Legacy Yemaya packages (during migration)
- 'yemaya/packages/*'
- 'yemaya/apps/*'
- 'yemaya/configs/*'
catalog:
# TypeScript toolchain
typescript: '^6.0.3'
tsup: '^8.0.2'
tsx: '^4.7.1'
# Testing
vitest: '^2.0.0'
'@vitest/coverage-v8': '^2.0.0'
playwright: '^1.42.0'
# Linting & Formatting
eslint: '^9.0.0'
prettier: '^3.2.5'
# React ecosystem
react: '^18.3.0'
react-dom: '^18.3.0'
# Node.js types
'@types/node': '^20.12.0'
# Validation
zod: '^3.22.4'
# Build tools
vite: '^6.0.0'
# Backend
fastify: '^4.26.0'
bullmq: '^5.34.0'
ioredis: '^5.4.0'
# Database
prisma: '^5.10.0'
'@prisma/client': '^5.10.0'
Root .npmrc Configuration:
# Dependency Resolution
strict-peer-dependencies=true
auto-install-peers=true
resolve-peers-from-workspace-root=true
# Hoisting Configuration
shamefully-hoist=true
public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=vite
public-hoist-pattern[]=vitest
public-hoist-pattern[]=@nx/*
# Performance
store-dir=~/.pnpm-store
prefer-offline=true
# Scripts
enable-pre-post-scripts=true
# Registry
registry=https://registry.npmjs.org/
# Git
git-checks=true
# Lockfile
use-lockfile-v6=false
Root package.json Structure:
{
"name": "oshun",
"private": true,
"version": "0.0.0",
"type": "module",
"packageManager": "pnpm@10.25.0",
"engines": {
"node": ">=18.0.0",
"pnpm": ">=9.0.0"
},
"scripts": {
"build": "nx run-many -t build",
"test": "nx run-many -t test",
"lint": "nx run-many -t lint",
"typecheck": "nx run-many -t typecheck",
"clean": "nx reset && rimraf node_modules",
"prepare": "husky"
}
}
Migration Strategy#
From:
- Lilith: Yarn workspaces + npm hybrid
- Yemaya: pnpm standalone
To:
- Oshun: Unified pnpm workspace at root
Migration Commands:
# Step 1: Backup existing lock files
cp lilith/yarn.lock ~/lilith-yarn.lock.backup
cp lilith/package-lock.json ~/lilith-package-lock.backup
cp yemaya/pnpm-lock.yaml ~/yemaya-pnpm-lock.backup
# Step 2: Remove old package manager artifacts
rm -f lilith/yarn.lock
rm -f lilith/package-lock.json
rm -f yemaya/pnpm-lock.yaml
# Step 3: Remove all node_modules
find . -name "node_modules" -type d -prune -exec rm -rf {} +
# Step 4: Create root configuration
# (Create pnpm-workspace.yaml, .npmrc, package.json as specified above)
# Step 5: Install all dependencies
pnpm install
# Step 6: Verify installation
pnpm ls --depth 0
pnpm why <package-name> # Debug specific packages
Rollback Plan: If critical issues discovered:
- Remove root pnpm-lock.yaml and node_modules
- Restore original lock files from backup
- Reinstall with original package managers
- Investigate issues before retrying
Testing Strategy#
Pre-Migration Verification:
- Capture current
npm lsoryarn listoutput - Record all package versions
- Run full test suite, record results
Post-Migration Verification:
- Compare package versions match pre-migration
- Run full test suite, compare results
- Verify build outputs are identical
- Test hot reload and development workflows
- Verify CI/CD pipeline runs successfully
Consequences#
Positive Consequences#
- ✅ Unified Tooling: Single package manager across entire monorepo
- ✅ Disk Savings: ~60% reduction in node_modules disk usage
- ✅ Faster CI/CD: Reduced install times improve pipeline efficiency
- ✅ Dependency Safety: Strict mode prevents phantom dependency bugs
- ✅ Version Consistency: Catalog ensures consistent versions across packages
- ✅ Better Debugging: Clear dependency graph aids troubleshooting
- ✅ Nx Synergy: Optimized integration with Nx workspace features
Negative Consequences#
- ❌ Migration Effort: One-time effort to convert Lilith packages
- ❌ Learning Curve: Developers need pnpm familiarity
- ❌ Compatibility Work: Some packages may need fixes for strict mode
- ❌ Documentation Updates: Contribution guides need updating
Risks and Mitigation#
| Risk | Probability | Impact | Mitigation Strategy |
|---|---|---|---|
| Package compatibility issues | Medium | Medium | Test thoroughly, use shamefully-hoist for problematic packages |
| Developer productivity dip | Low | Low | Provide pnpm cheat sheet, office hours for questions |
| CI/CD pipeline failures | Medium | Medium | Test pipeline changes in separate branch first |
| Build output differences | Low | High | Compare build artifacts before/after migration |
Compliance and Security#
Security Implications#
- Lockfile Integrity: pnpm-lock.yaml provides deterministic installs
- Audit Support:
pnpm auditscans for vulnerabilities - Supply Chain: Strict mode reduces attack surface from phantom dependencies
- Reproducibility: Content-addressable storage ensures identical packages
Compliance Requirements#
- Deterministic Builds: pnpm lockfile ensures reproducible installations
- Audit Trail: Package installation is fully logged
- License Compliance:
pnpm licensesreports all dependency licenses
Monitoring and Observability#
Metrics to Track#
- Install Time: Track
pnpm installduration in CI/CD - Disk Usage: Monitor ~/.pnpm-store and node_modules sizes
- Cache Hit Rate: pnpm store cache effectiveness
- Dependency Count: Total dependencies across workspace
Alerting Strategy#
- Install Failures: Alert on
pnpm installerrors in CI - Security Vulnerabilities: Weekly
pnpm auditruns - Disk Growth: Alert if store exceeds 5GB
Related Decisions#
Upstream Dependencies#
- ADR-0001: Git Repository Consolidation (single repo enables unified package management)
- Nx Adoption: Nx workspace requires consistent package manager
Downstream Impacts#
- CI/CD Configuration: Pipelines must use pnpm commands
- Docker Builds: Dockerfiles updated to use pnpm
- Developer Setup: Onboarding docs updated for pnpm
- IDE Configuration: VS Code/WebStorm settings for pnpm
References#
External Resources#
- pnpm Documentation
- pnpm Workspace
- pnpm vs npm vs Yarn Benchmark
- Nx + pnpm Integration
- Content-Addressable Storage Explained
Internal Resources#
- ADR-0001: Git Repository Consolidation
- Yemaya pnpm configuration —
yemaya/pnpm-workspace.yaml(pre-consolidation source repo) - Yemaya
.npmrc—yemaya/.npmrc(pre-consolidation source repo)
Revision History#
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-10 | Development Team | Initial version |