UDR Roadmap
Project Status (2026-08-05)
| Metric |
Value |
| Resolution ecosystems |
27 total (18 resolvable + 7 query-only + 2 internal; 25 active) |
| Solver |
AutoSolver (default) — profiles the graph and picks Z3 / PubGrub / Hybrid per workload; per-ecosystem isolation |
| ForkingResolver |
Cross-solver validator — on failure, runs the alternate solver (Z3 ↔ PubGrub) and confirms conflicts |
| CLI commands |
26 |
| Lock file |
udr.lock v2.1 with workspace, cross-eco, target sections |
| Tests |
4382 (3811 unit + 96 integration + 392 e2e + 83 others); 3804 unit passing, 5 skipped, 2 xfailed |
| Coverage threshold |
57% (enforced CI + pre-commit) |
| Architecture violations |
0 (enforced CI + pre-commit) |
| Ruff violations |
0 in backend/ |
| Missing docstrings |
0 — all D categories resolved (D102, D205, D401, D107, D413, D400, D417). |
What We Built — Historical Evolution
Phase 0 — Foundation (Solver + Pipeline)
| Item |
Status |
Details |
Z3 SAT solver (ConflictResolver) |
✅ |
2660-line SAT encoding with SCC partitioning, cross-solver validation, CUDA conflict rules |
| PubGrub solver (Rust-backed) |
✅ |
Opt-in via USE_PUBGRUB_SOLVER=true; pure-Python fallback |
| ForkingResolver |
✅ |
Repurposed as cross-solver validator — runs the alternate solver (Z3 ↔ PubGrub) on failure and confirms conflicts |
| Platform markers (PEP 508) |
✅ |
3-layer pipeline: PyPI client → Aggregator → BFS filtering |
| Content-addressed cache |
✅ |
SHA256 blob store with git-like sharding, GC, corruption detection |
| 20 ecosystem layer sync |
✅ |
All ecosystems wired through enum → manifest → client → settings → aliases |
Phase 1 — Data Source Clients (Ecosystem Coverage)
Clients with real registry API calls: PyPI, npm, Crates, Maven, GoModules, RubyGems, Packagist, Conda, APT, APK, NuGet, CocoaPods, Homebrew, Pub, Gradle, Swift, Hex, Haskell
Query-only (real registry API, no SAT traversal): Docker, Vcpkg, Terraform, Conan, Nix, Guix, Helm
| Ecosystem |
Manifest |
Lock Tree |
Resolver |
Updater |
Notes |
| npm |
✅ package.json |
✅ package-lock, pnpm-lock, yarn.lock |
✅ |
✅ |
Semaphore(10), ETag cached |
| crates |
✅ Cargo.toml |
✅ Cargo.lock |
✅ |
✅ |
cksum integrity |
| rubygems |
✅ Gemfile, .gemspec |
✅ Gemfile.lock |
✅ |
✅ |
sha integrity |
| packagist |
✅ composer.json |
✅ composer.lock |
✅ |
✅ |
|
| pypi |
✅ pyproject.toml, requirements.txt, Pipfile |
✅ poetry.lock, uv.lock |
✅ |
✅ |
Wheel tag awareness, marker eval |
| hex |
✅ mix.exs |
✅ mix.lock |
✅ |
✅ |
|
| gomodules |
✅ go.mod |
✅ go.sum (lock source) |
✅ |
✅ |
Replace, workspace, GOPROXY auth |
| gradle |
✅ build.gradle / .kts |
— |
✅ |
✅ |
11 configurations |
| swift |
✅ Package.swift |
✅ Package.resolved |
✅ |
✅ |
GitHub API rate-limited |
| haskell |
✅ *.cabal, stack.yaml, cabal.project |
— |
✅ |
✅ |
|
| pub |
✅ pubspec.yaml |
— |
✅ |
✅ |
|
| homebrew |
✅ Brewfile |
✅ Brewfile.lock.json |
✅ |
✅ |
|
| apt |
✅ apt-packages.txt (text) |
— |
✅ |
✅ |
Truncated at 100 |
| apk |
✅ apk-packages.txt (text) |
— |
✅ |
✅ |
|
| conda |
✅ environment.yml |
— |
✅ |
— |
pip deps correctly tagged |
| cocoapods |
✅ Podfile |
✅ Podfile.lock |
✅ |
✅ |
|
| maven |
✅ pom.xml |
— |
✅ |
✅ |
XML updater, no SNAPSHOT |
| nuget |
✅ packages.config |
— |
✅ |
— |
|
| nix |
✅ default.nix, shell.nix, flake.nix |
✅ flake.lock |
— |
— |
Query-only |
| guix |
✅ guix.scm, manifest.scm |
— |
— |
— |
Query-only |
| helm |
✅ Chart.yaml |
✅ Chart.lock |
— |
— |
Query-only |
| docker |
✅ Dockerfile |
— |
— |
— |
Query-only |
| terraform |
✅ *.tf, .terraform.lock.hcl |
— |
— |
— |
Query-only |
| vcpkg |
✅ vcpkg.json |
— |
— |
— |
Query-only |
| conan |
✅ conanfile.py |
— |
— |
— |
Query-only |
Phase 2 — P0/P1/P2/P3 Gap Closure (36 items, all closed, 2026-07-14)
P0 — Correctness (5/5):
- split("@")[0] → split("@", 1)[-1] for scoped npm packages
- asyncio.gather hardened at 6 sites with return_exceptions=True
- Auth middleware except Exception: pass → logger.exception()
- manifest_detector.py silent pass → logged warning
- except (TimeoutError, Exception) split into separate handlers
P1 — Performance (6/6):
- Ecosystem probing: partial results preserved on timeout
- 45× rglob → single rglob("*") walk (O(N×M) → O(N))
- O(n²) graph node scans → O(1) lookup dict
- DictCache debounce (2s write coalescing)
- Sequential CVE POST → parallel asyncio.gather
- Per-call ThreadPoolExecutor → reusable instance
P2 — Code Quality (10/10):
- 21 silent except Exception: pass → logged across 16 files
- 29 dead functions removed from api/routes/system.py (1491→559 lines)
- 20 manifest updaters extracted from cli/shared.py (1240→494 lines)
- _lock() monolith extracted into 6 helpers (615→90 lines)
- 7 module-level env vars centralized to settings/__init__.py
- 8 unregistered env vars registered + 4 unused import os removed
- Layer violation (api→cli) fixed
- 293 missing type annotations added
- 62 getattr(args, ...) calls → typed args.attr
- 4 duplicate function pairs consolidated
P3 — Test Coverage (7/7):
- pubgrub_core.py: 101 new tests (found 3 bugs → xfail)
- policy_engine.py: 47 new tests (10 rule types)
- pinning.py: 26 new tests
- ConflictResolver: 51 new method tests
- test_pipeline.py: e2e user workflow
- SBOM tests: deferred (thin wrapper)
- Plugin contract tests: all 5 plugins verified
P4 — Nice-to-Have (8/8):
- PubGrub flat-dict dependency constraint fix
- Blocking I/O → async in system.py
- Temp directory leak fix (mkdtemp → try/finally)
- SSRF guard (_validate_external_url)
- Background task tracking
- Version parse logging
- Lazy settings (PEP 562 __getattr__, 96+ env vars)
- Immutable CONFLICT_RULES tuple
Phase 3 — Q1-Q43 (191 findings, all fixed, 2026-07-17)
Key bug fixes across all 43 questions:
- Q1:
is_compatible_version logs instead of silently failing on non-PEP-440 versions (utils.py)
- Q2: Per-ecosystem isolation
break→continue (resolve.py:903)
- Q3: Hardened repository regression tests (39 tests: Semaphore, Go v-strip, SpecifierSet wrap, 6 repo smoke tests)
- Q4: Lock file ecosystem-qualified package keys
- Q5: Content-sniffing with PEP 508 validation
- Q6: Chart.lock and flake.lock reacheable; import-order plugin fix; Podfile.lock parser
- Q7: OS/arch constraint pipeline completed (dead code→live)
- Q8: Unbounded asyncio.gather → Semaphore guarded
- Q9: go.sum dead code removed
- Q10: Plugin import-order bug fixed
- Q11: udr.lock removed from MANIFEST_PATTERNS
- Q12: 94 cross-eco tests for all 21 untested ecosystems
- Q13: npm/yarn workspaces, Cargo [workspace], Go go.work
- Q14: Conda+pip correctly tags pip deps as pypi
- Q15: Go replace consumed, go.work parser, GOPROXY_AUTH_TOKEN
- Q16: Wheel platform tag enforcement (
check_platform_compatibility)
- Q17: Optional deps
--with-dev/--without-optional
- Q18: API key
is_active filter is→== (auth bypass fix)
- Q19: Graceful shutdown with signal handlers + context managers
- Q20: Docker/Vcpkg/Terraform/Conan/Helm real API calls (no stubs)
- Q21:
asyncio.run() inside event loop → await asyncio.wait_for
- Q22: Desktop/API sync (Check tab, CORS, API key)
- Q23: Docker deployment (SECRET_KEY, env vars, HEALTHCHECK)
- Q24: All 14 env vars registered in lazy settings
- Q25:
pytest-timeout in dev deps, global 120s timeout
- Q26: Coverage includes data_sources (removed omit)
- Q27: Nested transaction fix (remove inner
db.commit())
- Q28: Sentry
traces_sample_rate=1.0→0.1, send_default_pii=False
- Q29: Thread/Executor lifecycle leaks (try/finally guards)
- Q30: Race conditions (throttle, circuit breaker, cache, API key)
- Q31: Subprocess calls with timeout (22 sites)
- Q32: fcntl.flock with timeout + staleness check
- Q33: Connection pooling (reuse aiohttp.ClientSession)
- Q34: Plugin system (Hex typo, dual registration, validation, thread-safety, lifecycle)
- Q35: Database integrity (CASCADE, lazy→selectinload, size/overflow, SQLite JSON)
- Q36: Security hardening (27 items: timing attack, JWT, plaintext keys, CSRF, MD5→SHA256)
- Q37: Manifest parsers (14 broken parsers fixed)
- Q38: Settings/Config (11 inconsistencies fixed)
- Q39: Data source clients (8 bugs fixed)
- Q40: Utilities (logging, dead code, purl, typo)
- Q41: Documentation audit (13 stale claims fixed)
- Q42: Scanner gaps (macOS disks, APT truncation, race)
- Q43: Testing gaps (system_scanner 23→40 tests, test_comprehensive uses create_solver)
Phase 4 — Q44-Q47 (Claim Audit, 2026-07-18)
- Q44: Doc count mismatches fixed (API endpoints 63→54, exports 12→15, ecosystems 20→22)
- Q45: Cross-eco dependency resolution end-to-end (3 solver tests + E2E tests)
- Q46: EVIDENCE.md created (188 claims, 188/188 verified)
- Q47: Real-repo evidence (6 repos, 5,455 packages, 0 errors)
Phase 5 — Cross-Ecosystem Upgrade (all complete)
| # |
Item |
Status |
| 5.1 |
Batch/sharded SAT solver |
✅ SCC graph partitioning, topological resolution |
| 5.2-5.11 |
Manifest updaters (all 18 ecosystems) |
✅ go.mod, cabal, mix.exs, build.gradle, Package.swift, pubspec.yaml, Brewfile, pom.xml, apt/apk |
| 5.12 |
Incremental resolution |
✅ resolution_hash per package |
| 5.13 |
PubGrub solver integration |
✅ Opt-in via USE_PUBGRUB_SOLVER=true |
Phase 6 — Cross-Compilation & Offline
| # |
Item |
Status |
| 6.1 |
--target/--platform flags |
✅ OS/arch/CUDA target override |
| 6.2 |
Automatic offline index population |
✅ SQLite auto-cache during fetches |
| 6.3 |
Per-ecosystem solver isolation |
✅ _group_by_ecosystem() |
| # |
Item |
Status |
| 7.1 |
Web frontend (vanilla JS SPA) |
✅ 8 pages, D3.js graph, OSV CVE scanning, Jest tests |
| 7.2 |
Desktop app (Electron→Tauri) |
✅ Electron works (43 tests, 82.88% coverage on testable modules), Tauri deferred |
| 7.3 |
VSCode extension |
✅ 13 commands, CI wired, tsconfig fixed |
| 7.4 |
GitHub Actions |
✅ lock-check.yml |
| 7.5 |
GitLab CI |
✅ .gitlab-ci.yml template |
| 7.6 |
Pre-commit hook |
✅ lock-check before commits |
Phase 8 — Compliance & Security
| # |
Item |
Status |
| 8.1 |
License compliance |
✅ check --license |
| 8.2 |
CVE scanning |
✅ check --cve (OSV) |
| 8.3 |
CVE auto-fix |
✅ update --fix-cve |
| 8.4 |
SBOM generation |
✅ SPDX 2.3 / CycloneDX 1.5 |
| 8.5 |
Supply chain attestation |
✅ lock --sign, SLSA provenance |
| 8.6 |
Policy engine |
✅ 10 rule types |
Phase 9 — Ecosystem Deepening (all complete)
| # |
Item |
Status |
| 9.1 |
Nix/Guix |
✅ Manifests, lock parsers, 24 tests |
| 9.2 |
Vcpkg |
✅ C/C++ package manager |
| 9.3 |
Conan |
✅ C/C++ (another) |
| 9.4 |
Helm/chart |
✅ Kubernetes manifests |
| 9.5 |
Terraform provider locks |
✅ .terraform.lock.hcl |
| 9.6 |
Dockerfile FROM parsing |
✅ Docker Hub API v2 |
Phase 10 — Developer Experience (all complete)
| # |
Item |
Status |
| 10.1 |
Dependency graph |
✅ D3.js force-directed |
| 10.2 |
udr why |
✅ Dependency chain + constraint trace |
| 10.3 |
udr diff |
✅ Lock file comparison |
| 10.4 |
udr outdated |
✅ Stale package detection |
| 10.5 |
Shell completions |
✅ Bash/zsh/fish |
| 10.6 |
Man page |
✅ docs/man/udr.1 |
Remaining Work
Skipped Items (No Current Plan To Implement)
These items were evaluated and deliberately skipped because the effort does not justify the gain for UDR's core value proposition (cross-ecosystem resolution):
| Item |
Effort |
Rationale |
| Concurrent version support (Cargo-style) |
4-8 week architectural rework across SAT encoding, PubGrub API, BFS dedup, lock file format |
Only 1/20 ecosystems (Cargo) needs it. Cargo's existing resolution already works. |
| Virtual package/Provides resolution |
Solver changes for 2/20 ecosystems |
APT/APK parse provides data but APT/APK are flat-resolved (no cross-eco). Rare even in Debian. |
| Single lighter-weight SAT backend (PySAT/resolvo) |
Evaluate + integrate + maintain |
Z3 works. PubGrub is the strategic path. ForkingResolver already wraps both. Adding a third backend increases maintenance burden. |
| CLI consolidation (26→9 commands) |
Breaking change, doc rewrite, deprecation cycle |
Users learn 3-5 commands anyway. Breaking muscle memory hurts more than 26 commands. |
| Plugin marketplace |
Infrastructure, registry, discovery |
Zero community plugins exist. Build the API first, marketplace follows. |
| WASM frontend (browser-side resolver) |
Compile entire resolver to WASM |
No clear user need. Current web frontend works via REST API. |
| Desktop Tauri rewrite |
Full rewrite of Electron app |
Electron works (43 tests pass). Desktop adoption is niche. |
| Benchmark regression suite |
pytest-benchmark + historical tracking |
Existing scripts/benchmark.py + weekly CI benchmark workflow provide basic coverage. No direct user benefit. |
| Coverage 57%→65% |
Hardest 10% takes 90% effort |
Current 57% is green. Focus on high-risk paths rather than line count. |
| Full incremental re-resolution (skip BFS) |
Rewrite BFS to track subgraph changes |
Resolution hash caching already skips SAT for unchanged subtrees. BFS walk is not the bottleneck. |
| Man page |
Writing + packaging |
udr --help + CLI.md serve the same purpose. |
Nice-to-Have (If Someone Wants To Build)
| Item |
Effort |
Gain |
Status |
| Ruff docstrings (221 missing) |
1-2 hours per session, incremental |
Code clarity, ruff compliance |
✅ All 0 D violations — fixed across 54 files |
| Type stubs (.pyi) for orchestrator/ and core/ |
2-3 days |
Better IDE experience for library consumers |
✅ .pyi stubs created for orchestrator/ (12 exports) and core/ (21 lazy exports) |
| API/CLI parity: add /outdated, /diff, /why, /graph, /verify |
— |
— |
✅ All 5 endpoints exist |
| Structured error types (ResolutionError hierarchy) |
— |
— |
✅ Exists (P2 #25) |
| Prometheus metrics endpoint |
— |
— |
✅ Wired via Instrumentator (P2 #23) |
| OpenTelemetry spans |
— |
— |
✅ Manual spans on 3 critical paths (P2 #24) |
| Ecosystem version normalization table |
— |
— |
✅ Pre-release + multi-format in constraint_normalizer.py (P2 #19) |
| Ecosystem aliases |
— |
— |
✅ sanitize_ecosystem_name handles all common aliases (P2 #21) |
Release Milestones
| Version |
Focus |
Status |
Target |
| v1.3 |
Core resolution, 25 ecosystems, CLI+API, desktop app |
✅ Released |
Q3 2026 |
| v1.4 |
AutoSolver, cross-solver validation, ContentAddressedCache, platform markers, P0-P4 gap closure, Q1-Q43 fixes, Phase 5-10 complete, doc rewrite, accuracy hardening |
✅ Released |
2026-07-23 |
| v1.4.1 |
PubGrub prerelease correctness, duplicate-constraint merging, --target marker overrides, JSON lock-failure output, CUDA-index-aware resolution for PyPI torch, setup.py/setup.cfg parser, npm underscore mangling fix, e2e + Windows CI fixes, undici security bump |
✅ Released |
2026-08-06 |
| v1.4.2 |
versions/dependencies CLI commands, check exit codes, combinable checks, API pinning (block/pin/pin_mode), graph --from-lock, update --all, index build hosting manifest, optional-dep groups excluded by default, AutoSolver crash isolation, PEP 440 wildcard exclusions, golden regression matrix, /scan/local manifest-content mode, docs refresh |
🚀 Released |
2026-08-12 |
| v1.4.3 |
Windows desktop build hang root-caused and fixed (python-magic import blocks PyInstaller on current runner images), CodeQL path-injection hardening for /scan/local + /scan/upload, @hono/node-server security bump in golden fixture, all dependabot + code-scanning alerts closed |
🚀 Released |
2026-08-13 |
| v1.5 |
Remaining deferred items, community plugin marketplace, benchmark regression suite |
🔮 Next |
Q4 2026 |
| v2.0 |
Source repo URL + commit hash enrichment, desktop Tauri evaluation |
🔮 Planned |
Q1 2027 |
Key Strategic Decisions
- Plugin-first for ecosystem coverage — Not more bespoke clients. Define the plugin interface first, let the community fill in ecosystems.
- Library API surface =
orchestrator/ + core/ — Everything else (data_sources/, manifest_detector.py, database/) is internal with no stability guarantees.
- Client contract tests as gate — No new client lands without passing the standard suite (
test_client_contract.py).
- Desktop bundles offline indexes + local API — Not a separate codebase. Electron shell wraps
udr serve as subprocess.
- API mirrors CLI — Every CLI command should have a corresponding API endpoint.