AgentSkillsCN

dependency-risk-planner

在规划阶段进行事前依赖风险评估。在选择库、设计架构,或搭建新项目时,在编写代码之前使用。可有效预防依赖地狱、版本冲突,以及废弃库带来的陷阱。尤其适用于依赖关系繁杂的 ML/AI 项目。

SKILL.md
--- frontmatter
name: dependency-risk-planner
description: Pre-flight dependency risk assessment for the planning phase. Use BEFORE writing code when choosing libraries, designing architecture, or setting up a new project. Prevents dependency hell, version conflicts, and abandoned-library traps. Especially critical for ML/AI projects with heavy dependencies.
argument-hint: "[library-name or project-description]"

Dependency Risk Planner

Run this assessment BEFORE writing code. Every hour spent here saves days of debugging downstream. This skill exists because we learned the hard way: a dead library (Coqui TTS), a removed API (torchaudio.info()), and invisible transitive dependencies cost weeks of debugging that proper planning would have prevented entirely.

When to Use This Skill

  • Evaluating a library before adding it to the project
  • Starting a new project and choosing its dependency stack
  • Planning architecture around ML models or external services
  • Designing a deployment strategy for multiple environments
  • Reviewing an existing dependency stack for risk

Protocol

Phase 1: Dependency Health Audit

For EACH candidate library, research and answer these questions. Do not skip any. A single "red" answer can mean weeks of wasted work later.

1.1 Maintainer Viability

QuestionHow to CheckRed Flag
Is the maintainer an individual or a company?GitHub org page, about pageCompany (can shut down overnight)
Is the company/person financially stable?News, Crunchbase, layoff trackersRecent layoffs, no funding, pivoting
When was the last release?PyPI/npm release dates>6 months ago
When was the last commit to main?GitHub commits tab>3 months ago
Are issues being responded to?GitHub issues, response timeRising open count, no maintainer replies
Is there a clear successor if this dies?README, community forksNo alternative exists

Action: If any red flag fires, immediately research alternatives. Document the risk and the fallback plan BEFORE proceeding.

Real example: Coqui AI shut down in 2024. Their TTS library was the best open-source voice cloning solution. After shutdown: no releases, stale dependency pins, no Python 3.12 support. The entire installation process broke and required a --no-deps workaround with manually-managed transitive dependencies. Had we assessed maintainer viability in the planning phase, we would have either chosen an alternative or designed an abstraction layer from day one.

1.2 Dependency Tree Health

QuestionHow to CheckRed Flag
Does it pin exact versions of transitive deps?pip show -v <pkg>, setup.cfg/pyproject.tomlExact pins (==) on transitive deps
Do its pins conflict with your other deps?pip install --dry-runVersion conflicts
How many transitive deps does it pull in?pipdeptree -p <pkg>>20 transitive deps
Does it depend on C extensions that need compilation?Install on clean envBuild failures on different platforms
Can you install it with --no-deps and provide deps yourself?Try itImport errors at runtime

Action: Run pip install <pkg> --dry-run in a clean venv BEFORE committing to the library. If it takes >2 minutes to resolve, that's a warning sign.

Real example: pip install TTS backtracks through 30+ dependency versions for 10+ minutes and often fails entirely. The fix is pip install TTS==0.22.0 --no-deps, but this means you must manually discover and provide every transitive dependency (numpy, scipy, soundfile, librosa, scikit-learn, einops, unidecode, num2words, coqpit). Some of these only surface at runtime, not at import time.

1.3 Python/Runtime Compatibility

QuestionHow to CheckRed Flag
What Python versions does it support?pyproject.toml, setup.cfg, CI matrixMissing latest Python version
Does it use deprecated stdlib modules?Search for distutils, imp, pipesUses modules removed in Python 3.12+
Does it work on your target OS?CI matrix, issue trackerWindows/macOS/Linux gaps
Does it work on your target hardware?GPU/CPU tests, issue trackerGPU-only with no CPU fallback

Action: If the library caps Python version support, that cap becomes YOUR project's cap. Document this constraint in pyproject.toml immediately:

toml
requires-python = ">=3.10,<3.12"  # Capped by <library-name>

Real example: TTS 0.22.0 uses distutils (removed in Python 3.12, PEP 632). This forced our entire project to cap at Python <3.12. Every developer and every deployment environment must use Python 3.10 or 3.11. This constraint was invisible until installation failed on Python 3.12.

1.4 API Stability

QuestionHow to CheckRed Flag
Is the API you need in the stable/public API docs?Official docs, not just tutorialsFunction only in examples, not API reference
Has this API changed in the last 2 major versions?CHANGELOG, migration guidesBreaking changes without deprecation warnings
Are there open issues about the API you plan to use?GitHub issues search"Breaking change", "removed", "deprecated"
Does the library distinguish stable vs experimental APIs?Docs, module namingNo stability guarantees anywhere

Action: Only depend on APIs that appear in the library's official stable API documentation. Utility functions, convenience wrappers, and "info" helpers are the first to be removed.

Real example: torchaudio.info() was a convenience function for getting audio metadata. It was removed in torchaudio 2.1+ without a direct replacement. The successor torchaudio.load() changed its backend to require torchcodec, which isn't auto-installed. The fix: use soundfile.info() instead (wraps libsndfile, a C library stable for 20+ years). The lesson: framework utility functions change with every major version; C library wrappers are stable for decades.

Phase 2: Architecture Decisions

Based on the audit results, make these architecture decisions BEFORE writing code.

2.1 Wrapper Pattern for Fragile APIs

For every dependency that scored any red flags in Phase 1, create an abstraction layer:

code
YOUR CODE ---> Your Wrapper Module ---> Fragile Library

Design rule: Your application code NEVER imports the fragile library directly. All access goes through your wrapper. When the library breaks, you fix ONE file.

Example structure:

code
src/utils/
  audio.py        # Wraps soundfile + torchaudio
  image.py        # Wraps Pillow + torchvision
  model_loader.py # Wraps library-specific model loading

Each wrapper function should:

  • Have a clear docstring explaining WHY it wraps (not just WHAT)
  • Note which underlying library it uses and why
  • Be the ONLY place that library is imported

Real example: We used torchaudio.info() directly in two files (cloner.py and lipsync.py). When torchaudio removed it, we had to find and fix both call sites. A single get_audio_duration() wrapper would have meant fixing one function.

2.2 Import Strategy for Optional Heavy Dependencies

For libraries that are heavy (GPU frameworks, ML models), use LOCAL imports inside functions, not module-level imports:

python
# GOOD: Module loads without GPU libraries installed
def generate_speech(text):
    import torch  # Local import - only needed when this function runs
    from TTS.api import TTS
    ...

# BAD: Module fails to import if torch isn't installed
import torch  # Module-level - breaks CLI, config, tests

Why this matters for testing: If you use module-level imports, mocker.patch("mymodule.torch") works. But if you use local imports (which you should), you must mock at the sys.modules level:

python
mocker.patch.dict("sys.modules", {"torch": mock_torch})

Document your import strategy in a project convention so every developer and every test follows the same pattern.

2.3 Transitive Dependency Inventory

If you plan to use --no-deps for any library (because its dependency tree is broken), you MUST build a complete inventory of what it actually imports at runtime.

Method:

  1. Install the library with --no-deps
  2. Run a comprehensive smoke test that exercises every code path you use
  3. Record every ModuleNotFoundError
  4. Add each missing package to your explicit dependency list
  5. Repeat until no more import errors

Do NOT rely on the library's setup.cfg or pyproject.toml for this list. Dead libraries often have wrong or incomplete dependency declarations. Runtime testing is the only reliable method.

Create a smoke test file that verifies all transitive deps are importable:

python
# tests/test_smoke_imports.py
"""Import smoke tests - no mocking, no GPU needed."""

def test_tts_deps():
    """Verify all TTS transitive dependencies are installed."""
    import coqpit
    import einops
    import librosa
    import num2words
    import numpy
    import scipy
    import soundfile
    import unidecode

def test_ml_framework():
    import torch
    import torchaudio

2.4 Environment Compatibility Matrix

Plan your supported environments BEFORE writing code. Each environment variation is a potential failure point.

DimensionOptionsDocument In
Python version3.10, 3.11 (NOT 3.12+)pyproject.toml requires-python
OSLinux, macOS, WindowsCI matrix, install scripts
GPUNVIDIA (CUDA), CPU-onlyConfig profiles, fallback code
DeploymentLocal, RunPod, DockerInstall guide, Dockerfiles
venv toolvenv, condaInstall scripts

For each supported environment, plan:

  • An install script or Dockerfile
  • A verification command (avatar status, pytest --smoke)
  • Known gotchas documented in the install guide

Real example: RunPod pods ship with Python 3.12 as the system default. We didn't plan for this, so the first RunPod deployment failed. The fix (creating a Python 3.11 venv at /workspace/venv311) should have been in the deployment plan from day one.

Phase 3: Dependency Specification

Write these artifacts BEFORE writing application code.

3.1 Pinning Strategy

Decide your pinning strategy per dependency category:

CategoryStrategyExample
Dead library (no maintainer)Pin exact version foreverTTS==0.22.0
Active library, stable APIPin major versiontorch>=2.0,<3.0
Stable C wrapperFloat with minimumsoundfile>=0.12
Transitive dep (manual)Float with minimumnumpy>=1.24
Dev/test toolFloat with minimumpytest>=7.4

Never leave a dependency completely unpinned (bare numpy is dangerous).

3.2 Install Script Design

Write your install script BEFORE your application code. The install script is the first thing a new developer runs. If it fails, they never get to your code.

Install script checklist:

  • Detects and validates Python version (refuses incompatible versions)
  • Creates venv with --clear flag (prevents contamination)
  • Verifies venv Python version after activation (catches inheritance bugs)
  • Handles broken dependency trees (--no-deps workarounds)
  • Installs project with --no-deps if needed
  • Checks for system dependencies (FFmpeg, CUDA, etc.)
  • Creates required directories
  • Runs a quick verification (smoke test, not full test suite)
  • Prints clear summary with next steps

Every workaround in the script must have a comment explaining WHY:

bash
# Coqui AI shut down in 2024. Their TTS package has stale dependency
# pins that cause pip to backtrack for 10+ minutes. --no-deps skips
# the broken dependency tree. See docs/LESSONS_LEARNED.md.
pip install TTS==0.22.0 --no-deps

3.3 Documentation Artifacts

Create these documents BEFORE writing application code:

  1. INSTALLATION_GUIDE.md: Step-by-step with known gotchas
  2. Known Issues table: At the top of the install guide, not buried at the bottom
  3. requirements.txt header: TL;DR install recipe + warnings
  4. LESSONS_LEARNED.md: Living document, updated as new issues surface

Phase 4: Test Planning for Dependencies

Plan your test strategy to catch dependency issues that unit tests with mocking will miss.

4.1 Three Test Layers

LayerWhat It TestsMocking?When to Run
Smoke importsAll packages importableNoneAfter every pip install
Unit testsBusiness logicFull mockingEvery commit
Integration testsReal library callsMinimalBefore release, on CI with GPU

The critical gap: Unit tests with full mocking pass even when dependencies are completely broken. The smoke import tests fill this gap without needing GPU or models.

4.2 Mock Pattern Documentation

If your codebase uses local imports (which it should for heavy deps), document the correct mock pattern so every test author uses it:

python
# CORRECT: Mock at sys.modules level for local imports
mocker.patch.dict("sys.modules", {"torch": mock_torch})

# WRONG: Fails with AttributeError when import is inside a function
mocker.patch("src.mymodule.torch", mock_torch)

# CORRECT: Simulate "library not installed"
mocker.patch.dict("sys.modules", {"torch": None})

# WRONG: Doesn't trigger ImportError for local imports
mocker.patch("src.mymodule.torch", side_effect=ImportError)

4.3 Dataclass/Interface Stability

If your interfaces use dataclasses, plan for field additions:

python
# Fragile: Adding a new required field breaks all existing call sites
@dataclass
class Result:
    success: bool
    data: dict
    error: str        # Adding this breaks every Result() call

# Resilient: New fields have defaults, existing code keeps working
@dataclass
class Result:
    success: bool
    data: dict
    error: Optional[str] = None  # Safe to add later

Rule: Every field that might be added later should have a default value. Required fields should only be things that are truly required from day one.

Output

After completing this assessment, produce:

  1. Risk Summary Table: Each dependency with health score (green/yellow/red)
  2. Architecture Decisions: Wrapper modules needed, import strategy, pinning strategy
  3. Action Items: Ordered list of what to create before writing application code
  4. Dependency Specification Files: requirements.txt, pyproject.toml with pins and comments

Additional Resources

For detailed case studies of every issue this skill prevents, see: