# 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 <package>' command
  
  if args.len < 1:
    echo "Usage: nip resolve <package> [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
