See the graph before changing the package.
Dependency Time Machine restore-validates one exact package candidate in two independent snapshots, compares its dependency impact, and leaves the checkout untouched.
Ask one precise what-if question
package-medic simulate Example.Package --to 2.0.0 ./MySolution.sln
# Include official NuGet risk evidence
package-medic simulate Example.Package --to 2.0.0 . --audit --deprecated --include-transitive --format json --output artifacts/example-2.0.0.simulation.jsonThe package must already be an effective direct PackageReference or centralPackageVersion. The candidate must be one exact NuGet version.
Know exactly what a pass means
Run independently for the committed baseline and candidate.
Compare versions, kinds, causal paths, risks, provenance, and Impact Gate policy.
Not run by default; opt in with --verify build.
Opt in with --verify test; runtime remains unverified.
PackageMedic 0.6 can opt into comparative build and test evidence. See the Verified experiments guide for the execution boundary, conservative verdicts, and structured test requirements.
Two snapshots, one immutable commit
- 1Require a clean tree
Resolve one immutable
HEADand reject tracked or untracked changes. - 2Build the baseline
Materialize, restore, and analyze snapshot A.
- 3Edit only the candidate
Materialize snapshot B, verify the XML and SHA-256 precondition, then replace only the encoded version value.
- 4Restore and compare
Analyze B and apply the same diagnostics, risk evidence, and Dependency Impact Gate.
- 5Clean and verify
Delete owned snapshots without following links and recheck the original worktree before reporting.
The editor preserves encoding, BOM, line endings, comments, spacing, quote style, and attribute order. It refuses dynamic, conditional, ambiguous, external, transitive-only, or unsafe XML declarations instead of guessing.
Verdicts and exit codes
| Verdict | Exit | Meaning |
|---|---|---|
pass | 0 | Restore and comparison completed with no observed rejection. |
noChange | 0 | The declaration is NuGet-equivalent, or no package, diagnostic, setting, provenance, or risk delta was observed. |
reject | 1 | The candidate restore or a configured diagnostic/Impact Gate rejected the change. |
incomplete | 2 | An operational failure prevented a trustworthy conclusion. |
A version missing from reachable configured feeds is a complete rejection. Authentication, unavailable-source, unknown-restore, timeout, extraction, evaluation, audit, cleanup, or infrastructure failures are incomplete and never become a pass.
Lock files stay authoritative
Candidate restore respects RestoreLockedMode and trackedpackages.lock.json files. Time Machine never silently disables locked mode or rewrites the original lock file. A locked-mode conflict is reported as a complete rejection that requires an intentional lockfile update—not as binary incompatibility.
Private-feed state is opt-in
Each snapshot receives separate NuGet package, HTTP, plugin, CLI-home, app-data, user-home, and temporary caches. Secret environment variables are not inherited automatically.
package-medic simulate Contoso.Package --to 4.2.0 . --credential-env VSS_NUGET_EXTERNAL_FEED_ENDPOINTS --credential-env CONTOSO_FEED_TOKENEvery named variable must exist and is registered for output redaction. The option is repeatable. NuGet configuration still controls which sources restore may contact. Source policy accepts credential-free HTTPS URLs without queries or fragments, or the explicitlocal value.
A separate deterministic report contract
Simulation JSON schema version 2 separates repository, request, mutation, verification, comparison, rejection reasons, and operational errors. Reports contain no timestamps or temporary paths. Hypothetical results are not emitted as SARIF or uploaded by the GitHub Action.
{
"schemaVersion": 2,
"kind": "dependencySimulation",
"toolVersion": "0.6.0",
"repository": {
"headCommit": "0000000000000000000000000000000000000000",
"analysisTarget": "MySolution.sln",
"workingTreeRequiredClean": true
},
"request": { "packageId": "Example.Package", "candidateVersion": "2.0.0" },
"mutation": {
"packageId": "Example.Package",
"file": "Directory.Packages.props",
"line": 18,
"kind": "centralPackageVersion",
"beforeVersion": "1.5.0",
"candidateVersion": "2.0.0",
"affectedProjects": ["src/App/App.csproj"],
"noChange": false,
"sourceSha256Before": "1111111111111111111111111111111111111111111111111111111111111111",
"sourceSha256After": "2222222222222222222222222222222222222222222222222222222222222222"
},
"verification": {
"restore": "passed",
"build": "notRun",
"tests": "notRun",
"runtimeCompatibility": "notVerified",
"evidenceLevel": "restoreOnly",
"auditedVulnerabilities": false,
"auditedDeprecations": false,
"lockedMode": "notEnabled"
},
"comparison": {
"diagnosticSummary": { "added": 0, "resolved": 0, "severityChanged": 0 },
"diagnosticChanges": [],
"packageSummary": {
"added": 0, "removed": 0, "upgraded": 0, "downgraded": 0,
"uncomparableVersionChanges": 0, "directToTransitive": 0,
"transitiveToDirect": 0, "otherModified": 0
},
"packageChanges": [],
"riskSummary": {
"vulnerabilitiesIntroduced": 0, "vulnerabilitiesResolved": 0,
"deprecationsIntroduced": 0, "deprecationsResolved": 0,
"vulnerabilitiesPersistent": 0, "deprecationsPersistent": 0
},
"projectSettingsChanges": [],
"isComplete": true
},
"isComplete": true,
"verdict": "pass",
"rejectionReasons": [],
"errors": []
}Bounded for large repositories
Cost is approximately two independent snapshots and restores plus optional audits. Select the narrowest representative solution or project. Archive size, entries, expanded bytes, free space, extraction time, subprocess output, restore/evaluation time, graph traversal, assets, XML, and parallelism all have hard bounds; exceeding one returns incomplete.