PackageMedicDocs
Pull-request intelligence

Know what a package update brings with it.

The Dependency Impact Gate traces every changed transitive package to its direct cause, measures blast radius, and enforces source-trust and reproducibility policy before a pull request merges.

From a flat diff to a causal review

A package update is rarely one line in the resolved graph. PackageMedic builds a deterministic shortest path from every changed transitive package to the direct dependency responsible for it, separately for each project, framework, and runtime.

Causal dependency path
Contoso.Web 4.0.0
  -> Contoso.Transport 3.2.0
  -> Contoso.Json 2.1.0

When multiple direct packages reach the same transitive, one canonical path is selected and the other direct roots remain available in structured output. The maximum blast radius is the largest number of changed transitives attributed to one direct root.

Run the gate

Terminal
package-medic diff origin/main .
package-medic diff origin/main . --format json --output artifacts/impact.json
package-medic diff origin/main . --audit --deprecated --include-transitive

The gate is evaluated only after both graphs complete. Exit code 0 means the diagnostic threshold and Impact Gate passed; 1 means either gate failed; and2 means the comparison was operationally incomplete.

Define the repository boundary

.packagemedic.json
{
  "schemaVersion": 1,
  "impact": {
    "failOnDowngrade": true,
    "failOnDirectToTransitive": true,
    "maxAddedPackages": 40,
    "maxAddedTransitivePackages": 25,
    "failOnSourceChange": true,
    "failOnContentChange": true,
    "requirePackageSourceMapping": true,
    "requireLockedMode": true,
    "allowedSources": [
      "https://api.nuget.org/v3/index.json",
      "https://packages.example.com/v3/index.json"
    ]
  }
}
PropertyDefaultPolicy
failOnDowngradetrueReject resolved package downgrades.
failOnDirectToTransitivetrueReject losing explicit control of a formerly direct dependency.
maxAddedPackagesunsetLimit every package added by the comparison.
maxAddedTransitivePackagesunsetLimit dependency growth outside direct declarations.
failOnSourceChangetrueReject a source change or loss/gain of source evidence for a persistent package.
failOnContentChangetrueReject a SHA-512 change or loss/gain of hash evidence for the same package ID/version.
requirePackageSourceMappingfalseRequire effective repository source mapping with a usable pattern for every resolved package.
requireLockedModefalseRequire locked restore and a valid NuGet lock file inside the analysis root.
allowedSourcesemptyAllow only credential-free HTTPS sources or the explicit local value.

Source trust fails closed when requested

PackageMedic reads bounded source, content-hash, and signature-presence evidence from metadata produced by NuGet restore. It does not contact a private provenance service or expose credentials. Query- or fragment-qualified sources and metadata reached through a symbolic link or junction remain unknown rather than inheriting trust from a base URL. A persistent package losing previously observed source or hash evidence is itself a gated change, even without an explicit source allowlist. If an allowlist is active and a changed package's source cannot be established, the gate reports unknown provenance instead of assuming it is trusted.

Impact policy codes

CodeCondition
PMI001Package downgrade.
PMI002Direct dependency became transitive.
PMI003Total added-package budget exceeded.
PMI004Added-transitive budget exceeded.
PMI005Package source changed.
PMI006Source is unknown while an allowlist is active.
PMI007Source is outside the allowlist.
PMI008Multiple feeds lack effective repository Package Source Mapping.
PMI009Locked restore is disabled or its in-repository NuGet lock file is missing or invalid.
PMI010Same package ID/version has different SHA-512 content.

Review it in GitHub

GitHub Actions YAML
- uses: actions/checkout@v6
  with:
    fetch-depth: 0

- uses: GonzMeza/package-medic@v0.6.1
  with:
    mode: auto
    config: .packagemedic.json
    audit: 'true'
    deprecated: 'true'

The job summary shows pass/fail status, dependency growth, source changes, maximum blast radius, and each failed policy with its causal path. Workflow outputs includeimpact-gate-passed, impact-violations,impact-added-direct, impact-added-transitive,impact-max-blast-radius, impact-source-changes, andimpact-content-changes.

Auto mode is for the unprivileged pull_request event. PackageMedic rejectspull_request_target because its default checkout would compare the trusted base against itself; do not execute an untrusted PR head with privileged secrets.