# Dependency Resolver Integration Guide **Version:** 1.0 **Last Updated:** November 25, 2025 **Status:** Active Development --- ## Overview This guide explains how to integrate all components of the NIP dependency resolver into a cohesive system. It covers the complete resolution workflow from package specification to installed artifacts. --- ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ NIP CLI Interface │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Resolution Orchestrator │ │ - Coordinates all resolver components │ │ - Manages cache lifecycle │ │ - Handles error reporting │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Variant │ │ Graph │ │ Solver │ │ Unification │ │ Builder │ │ (CDCL) │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────┼─────────────────────┘ ▼ ┌──────────────┐ │ Cache │ │ (3-Tier) │ └──────────────┘ │ ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ L1 (Memory) │ │ L2 (CAS) │ │ L3 (SQLite) │ └──────────────┘ └──────────────┘ └──────────────┘ ``` --- ## Component Integration ### 1. Resolution Orchestrator The orchestrator coordinates all resolver components and manages the resolution workflow. ```nim # nip/src/nip/resolver/orchestrator.nim import ./types import ./variant import ./graph_builder import ./solver import ./conflict import ./build_synthesis import ./resolution_cache import ../cas/storage type ResolutionOrchestrator* = ref object cache: ResolutionCache casStorage: CASStorage repositories: seq[Repository] config: ResolverConfig ResolverConfig* = object enableCache: bool enableParallel: bool maxRetries: int timeout: Duration proc newResolutionOrchestrator*( casStorage: CASStorage, repositories: seq[Repository], config: ResolverConfig ): ResolutionOrchestrator = result = ResolutionOrchestrator( cache: newResolutionCache(casStorage, enabled = config.enableCache), casStorage: casStorage, repositories: repositories, config: config ) proc resolve*( orchestrator: ResolutionOrchestrator, rootPackage: string, constraint: string, variantDemand: VariantDemand ): Result[DependencyGraph, ResolutionError] = ## Main resolution entry point # 1. Check cache let repoHash = calculateGlobalRepoStateHash(orchestrator.repositories) orchestrator.cache.updateRepoHash(repoHash) let cacheKey = CacheKey( rootPackage: rootPackage, rootConstraint: constraint, repoStateHash: repoHash, variantDemand: variantDemand ) let cached = orchestrator.cache.get(cacheKey) if cached.value.isSome: return ok(cached.value.get) # 2. Build dependency graph let graphResult = buildDependencyGraph( rootPackage, constraint, variantDemand, orchestrator.repositories ) if graphResult.isErr: return err(graphResult.error) let graph = graphResult.get # 3. Solve constraints let solverResult = solve(graph) if solverResult.isErr: # Detect and report conflicts let conflicts = detectConflicts(graph) return err(ResolutionError( kind: ConflictError, conflicts: conflicts )) # 4. Synthesize builds for node in graph.nodes: let buildResult = synthesizeBuild(node, variantDemand) if buildResult.isErr: return err(buildResult.error) # 5. Cache result orchestrator.cache.put(cacheKey, graph) return ok(graph) ``` ### 2. CLI Integration Connect the orchestrator to the CLI interface. ```nim # nip/src/nip/cli/resolve.nim import ../resolver/orchestrator import ../resolver/types import ../cas/storage proc resolveCommand*(args: seq[string]): int = ## Handle 'nip resolve ' command if args.len < 1: echo "Usage: nip resolve [constraint]" return 1 let packageName = args[0] let constraint = if args.len > 1: args[1] else: "*" # Load configuration let config = loadResolverConfig() # Initialize components let cas = newCASStorage(config.casPath) let repos = loadRepositories(config.repoPath) let orchestrator = newResolutionOrchestrator(cas, repos, config) # Resolve dependencies echo fmt"Resolving {packageName} {constraint}..." let variantDemand = VariantDemand( useFlags: @[], libc: "musl", allocator: "jemalloc", targetArch: "x86_64", buildFlags: @[] ) let result = orchestrator.resolve(packageName, constraint, variantDemand) if result.isErr: echo "❌ Resolution failed:" echo formatError(result.error) return 1 let graph = result.get # Display results echo "✅ Resolution successful!" echo fmt"Packages: {graph.nodes.len}" echo "" echo "Installation order:" let sorted = topologicalSort(graph) for i, node in sorted: echo fmt" {i+1}. {node.packageId.name} {node.packageId.version}" return 0 ``` ### 3. Error Handling Integration Provide comprehensive error reporting. ```nim # nip/src/nip/resolver/error_reporting.nim import ./types import ./conflict proc formatError*(error: ResolutionError): string = ## Format resolution error for user display case error.kind: of ConflictError: result = "Dependency conflicts detected:\n\n" for conflict in error.conflicts: result &= formatConflict(conflict) result &= "\n" result &= "\nSuggestions:\n" result &= " • Try relaxing version constraints\n" result &= " • Use NipCell for conflicting packages\n" result &= " • Check for circular dependencies\n" of PackageNotFoundError: result = fmt"Package not found: {error.packageName}\n\n" result &= "Suggestions:\n" result &= " • Check package name spelling\n" result &= " • Update repository metadata: nip update\n" result &= " • Search for similar packages: nip search {error.packageName}\n" of BuildFailureError: result = fmt"Build failed for {error.packageName}:\n" result &= error.buildLog result &= "\n\nSuggestions:\n" result &= " • Check build dependencies\n" result &= " • Review build log for errors\n" result &= " • Try different variant flags\n" of TimeoutError: result = "Resolution timeout exceeded\n\n" result &= "Suggestions:\n" result &= " • Increase timeout: nip config set timeout 600\n" result &= " • Check network connectivity\n" result &= " • Simplify dependency constraints\n" ``` --- ## Workflow Examples ### Example 1: Simple Package Resolution ```nim # Resolve nginx with default settings let orchestrator = newResolutionOrchestrator(cas, repos, defaultConfig) let result = orchestrator.resolve( "nginx", ">=1.24.0", VariantDemand( useFlags: @["ssl", "http2"], libc: "musl", allocator: "jemalloc", targetArch: "x86_64", buildFlags: @[] ) ) if result.isOk: let graph = result.get echo fmt"Resolved {graph.nodes.len} packages" ``` ### Example 2: Complex Resolution with Caching ```nim # First resolution (cold cache) let start1 = cpuTime() let result1 = orchestrator.resolve("complex-app", "*", demand) let time1 = cpuTime() - start1 echo fmt"Cold cache: {time1 * 1000:.2f}ms" # Second resolution (warm cache) let start2 = cpuTime() let result2 = orchestrator.resolve("complex-app", "*", demand) let time2 = cpuTime() - start2 echo fmt"Warm cache: {time2 * 1000:.2f}ms" let speedup = time1 / time2 echo fmt"Speedup: {speedup:.2f}x" ``` ### Example 3: Conflict Handling ```nim let result = orchestrator.resolve("conflicting-app", "*", demand) if result.isErr: let error = result.error if error.kind == ConflictError: echo "Conflicts detected:" for conflict in error.conflicts: case conflict.kind: of VersionConflict: echo fmt" • {conflict.package1} requires {conflict.constraint1}" echo fmt" {conflict.package2} requires {conflict.constraint2}" of VariantConflict: echo fmt" • Incompatible variants for {conflict.packageName}" echo fmt" {conflict.variant1} vs {conflict.variant2}" # Suggest NipCell fallback echo "\nConsider using NipCell for isolation:" echo " nip cell create app1-env" echo " nip install --cell=app1-env conflicting-app" ``` --- ## Testing Integration ### Unit Tests Test each component independently: ```nim # Test variant unification suite "Variant Integration": test "Unify compatible variants": let v1 = VariantDemand(useFlags: @["ssl"]) let v2 = VariantDemand(useFlags: @["http2"]) let result = unifyVariants(v1, v2) check result.isOk check result.get.useFlags == @["ssl", "http2"] ``` ### Integration Tests Test complete workflows: ```nim # Test end-to-end resolution suite "Resolution Integration": test "Resolve simple package": let orchestrator = setupTestOrchestrator() let result = orchestrator.resolve("test-pkg", "*", defaultDemand) check result.isOk check result.get.nodes.len > 0 ``` ### Performance Tests Validate performance targets: ```nim # Test resolution performance suite "Performance Integration": test "Simple package resolves in <50ms": let orchestrator = setupTestOrchestrator() let start = cpuTime() let result = orchestrator.resolve("simple-pkg", "*", defaultDemand) let elapsed = cpuTime() - start check result.isOk check elapsed < 0.050 # 50ms ``` --- ## Configuration ### Resolver Configuration File ```kdl // nip-resolver.kdl resolver { version "1.0" cache { enabled true l1_capacity 100 l2_enabled true l3_enabled true l3_path "/var/lib/nip/cache.db" } performance { parallel_enabled false // Enable when ready max_parallel_jobs 4 timeout 300 // seconds max_retries 3 } repositories { update_interval "24h" verify_signatures true } variants { default_libc "musl" default_allocator "jemalloc" default_arch "x86_64" } } ``` --- ## Deployment Checklist ### Pre-Deployment - [ ] All unit tests passing - [ ] All integration tests passing - [ ] Performance benchmarks meet targets - [ ] Cache invalidation tested - [ ] Error handling comprehensive - [ ] Documentation complete ### Deployment - [ ] Deploy resolver components - [ ] Initialize cache database - [ ] Configure repositories - [ ] Set up monitoring - [ ] Enable profiling (optional) ### Post-Deployment - [ ] Monitor cache hit rates - [ ] Track resolution times - [ ] Collect error reports - [ ] Analyze performance metrics - [ ] Optimize based on real usage --- ## Monitoring and Observability ### Metrics to Track ```nim type ResolverMetrics* = object totalResolutions*: int successfulResolutions*: int failedResolutions*: int avgResolutionTime*: float cacheHitRate*: float conflictRate*: float proc collectMetrics*(orchestrator: ResolutionOrchestrator): ResolverMetrics = let cacheMetrics = orchestrator.cache.getMetrics() return ResolverMetrics( totalResolutions: orchestrator.totalResolutions, successfulResolutions: orchestrator.successCount, failedResolutions: orchestrator.failureCount, avgResolutionTime: orchestrator.totalTime / orchestrator.totalResolutions.float, cacheHitRate: cacheMetrics.totalHitRate, conflictRate: orchestrator.conflictCount.float / orchestrator.totalResolutions.float ) ``` ### Logging ```nim import logging # Configure logging let logger = newConsoleLogger(lvlInfo) addHandler(logger) # Log resolution events info("Starting resolution", packageName, constraint) debug("Cache lookup", cacheKey, cacheResult) warn("Conflict detected", conflictType, packages) error("Resolution failed", errorMessage, stackTrace) ``` --- ## Troubleshooting ### Common Issues **Issue:** Cache not working **Solution:** Check cache is enabled in config, verify CAS storage accessible **Issue:** Slow resolution **Solution:** Enable profiling, identify hot paths, optimize bottlenecks **Issue:** Conflicts not detected **Solution:** Verify conflict detection enabled, check conflict rules **Issue:** Memory usage high **Solution:** Reduce L1 cache capacity, enable LRU eviction --- ## Next Steps 1. **Complete Integration:** Connect all components in orchestrator 2. **Add CLI Commands:** Implement resolve, explain, conflicts commands 3. **Test End-to-End:** Run complete workflows with real packages 4. **Optimize Performance:** Profile and optimize hot paths 5. **Deploy and Monitor:** Deploy to production, track metrics --- **Document Version:** 1.0 **Last Updated:** November 25, 2025 **Status:** Active Development